
C语言如何计算联程
计算联程主要涉及指针操作、链表结构、递归算法。在C语言中,联程通常指的是链表这一数据结构的遍历和操作。链表是一种线性数据结构,其中每个元素都是一个节点,每个节点包含数据和一个指向下一个节点的指针。以下是对如何在C语言中计算联程的详细解释,包括创建链表、遍历链表以及进行各种操作的方法。
一、链表的基本概念和类型
链表是一种动态数据结构,可以在运行时动态地增加或减少节点。链表主要有以下几种类型:
- 单链表:每个节点只包含一个指向下一个节点的指针。
- 双向链表:每个节点包含两个指针,一个指向下一个节点,另一个指向前一个节点。
- 循环链表:最后一个节点的指针指向链表的头节点,形成一个环。
1、单链表
单链表是最基本的链表形式,每个节点包含一个数据和一个指向下一个节点的指针。
#include <stdio.h>
#include <stdlib.h>
// 定义链表节点结构
struct Node {
int data;
struct Node* next;
};
// 创建新节点
struct Node* createNode(int data) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = data;
newNode->next = NULL;
return newNode;
}
// 遍历链表
void printList(struct Node* head) {
struct Node* temp = head;
while (temp != NULL) {
printf("%d -> ", temp->data);
temp = temp->next;
}
printf("NULLn");
}
在这个例子中,创建了一个基本的单链表节点结构,并定义了创建新节点和遍历链表的函数。
2、双向链表
双向链表的每个节点包含两个指针,一个指向前一个节点,一个指向后一个节点。
#include <stdio.h>
#include <stdlib.h>
// 定义双向链表节点结构
struct DNode {
int data;
struct DNode* prev;
struct DNode* next;
};
// 创建新节点
struct DNode* createDNode(int data) {
struct DNode* newNode = (struct DNode*)malloc(sizeof(struct DNode));
newNode->data = data;
newNode->prev = NULL;
newNode->next = NULL;
return newNode;
}
// 遍历双向链表
void printDList(struct DNode* head) {
struct DNode* temp = head;
while (temp != NULL) {
printf("%d <-> ", temp->data);
temp = temp->next;
}
printf("NULLn");
}
在这个例子中,创建了一个基本的双向链表节点结构,并定义了创建新节点和遍历双向链表的函数。
3、循环链表
循环链表的最后一个节点指向头节点,形成一个环。
#include <stdio.h>
#include <stdlib.h>
// 定义循环链表节点结构
struct CNode {
int data;
struct CNode* next;
};
// 创建新节点
struct CNode* createCNode(int data) {
struct CNode* newNode = (struct CNode*)malloc(sizeof(struct CNode));
newNode->data = data;
newNode->next = NULL;
return newNode;
}
// 遍历循环链表
void printCList(struct CNode* head) {
if (head == NULL) return;
struct CNode* temp = head;
do {
printf("%d -> ", temp->data);
temp = temp->next;
} while (temp != head);
printf("(back to head)n");
}
在这个例子中,创建了一个基本的循环链表节点结构,并定义了创建新节点和遍历循环链表的函数。
二、链表的基本操作
链表的常见操作包括插入节点、删除节点、查找节点和修改节点等。
1、插入节点
插入节点有多种方式,可以在链表的头部、尾部或中间位置插入。
在头部插入节点
void insertAtHead(struct Node head, int data) {
struct Node* newNode = createNode(data);
newNode->next = *head;
*head = newNode;
}
在尾部插入节点
void insertAtTail(struct Node head, int data) {
struct Node* newNode = createNode(data);
if (*head == NULL) {
*head = newNode;
return;
}
struct Node* temp = *head;
while (temp->next != NULL) {
temp = temp->next;
}
temp->next = newNode;
}
在指定位置插入节点
void insertAtPosition(struct Node head, int data, int position) {
struct Node* newNode = createNode(data);
if (position == 0) {
newNode->next = *head;
*head = newNode;
return;
}
struct Node* temp = *head;
for (int i = 0; temp != NULL && i < position - 1; i++) {
temp = temp->next;
}
if (temp == NULL) return;
newNode->next = temp->next;
temp->next = newNode;
}
2、删除节点
删除节点也可以在头部、尾部或中间位置进行。
删除头节点
void deleteHead(struct Node head) {
if (*head == NULL) return;
struct Node* temp = *head;
*head = (*head)->next;
free(temp);
}
删除尾节点
void deleteTail(struct Node head) {
if (*head == NULL) return;
if ((*head)->next == NULL) {
free(*head);
*head = NULL;
return;
}
struct Node* temp = *head;
while (temp->next->next != NULL) {
temp = temp->next;
}
free(temp->next);
temp->next = NULL;
}
删除指定位置的节点
void deleteAtPosition(struct Node head, int position) {
if (*head == NULL) return;
struct Node* temp = *head;
if (position == 0) {
*head = temp->next;
free(temp);
return;
}
for (int i = 0; temp != NULL && i < position - 1; i++) {
temp = temp->next;
}
if (temp == NULL || temp->next == NULL) return;
struct Node* next = temp->next->next;
free(temp->next);
temp->next = next;
}
三、链表的高级操作
链表还可以进行一些高级操作,如反转链表、合并链表、检测环等。
1、反转链表
反转链表是将链表的节点顺序颠倒。
struct Node* reverseList(struct Node* head) {
struct Node* prev = NULL;
struct Node* current = head;
struct Node* next = NULL;
while (current != NULL) {
next = current->next;
current->next = prev;
prev = current;
current = next;
}
return prev;
}
2、合并两个有序链表
合并两个有序链表是将两个已经排序的链表合并成一个新的有序链表。
struct Node* mergeLists(struct Node* l1, struct Node* l2) {
if (l1 == NULL) return l2;
if (l2 == NULL) return l1;
if (l1->data < l2->data) {
l1->next = mergeLists(l1->next, l2);
return l1;
} else {
l2->next = mergeLists(l1, l2->next);
return l2;
}
}
3、检测链表是否有环
使用快慢指针的方法可以有效检测链表是否存在环。
int hasCycle(struct Node* head) {
if (head == NULL || head->next == NULL) return 0;
struct Node* slow = head;
struct Node* fast = head->next;
while (fast != NULL && fast->next != NULL) {
if (slow == fast) return 1;
slow = slow->next;
fast = fast->next->next;
}
return 0;
}
四、链表的应用场景
链表在实际应用中有很多场景,比如实现LRU缓存、处理大数相加、表达式求值等。
1、实现LRU缓存
LRU缓存是一种常见的缓存淘汰策略,可以使用双向链表和哈希表实现。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 定义缓存节点结构
struct CacheNode {
int key;
int value;
struct CacheNode* prev;
struct CacheNode* next;
};
// 定义LRU缓存结构
struct LRUCache {
int capacity;
int size;
struct CacheNode* head;
struct CacheNode* tail;
struct CacheNode hashTable;
};
// 创建新缓存节点
struct CacheNode* createCacheNode(int key, int value) {
struct CacheNode* newNode = (struct CacheNode*)malloc(sizeof(struct CacheNode));
newNode->key = key;
newNode->value = value;
newNode->prev = NULL;
newNode->next = NULL;
return newNode;
}
// 创建LRU缓存
struct LRUCache* createLRUCache(int capacity) {
struct LRUCache* cache = (struct LRUCache*)malloc(sizeof(struct LRUCache));
cache->capacity = capacity;
cache->size = 0;
cache->head = NULL;
cache->tail = NULL;
cache->hashTable = (struct CacheNode)malloc(capacity * sizeof(struct CacheNode*));
memset(cache->hashTable, 0, capacity * sizeof(struct CacheNode*));
return cache;
}
// 将节点移到头部
void moveToHead(struct LRUCache* cache, struct CacheNode* node) {
if (node == cache->head) return;
if (node->prev != NULL) node->prev->next = node->next;
if (node->next != NULL) node->next->prev = node->prev;
if (node == cache->tail) cache->tail = node->prev;
node->prev = NULL;
node->next = cache->head;
if (cache->head != NULL) cache->head->prev = node;
cache->head = node;
if (cache->tail == NULL) cache->tail = node;
}
// 添加节点到缓存
void addNode(struct LRUCache* cache, struct CacheNode* node) {
if (cache->size == cache->capacity) {
struct CacheNode* tail = cache->tail;
cache->tail = tail->prev;
if (cache->tail != NULL) cache->tail->next = NULL;
cache->hashTable[tail->key % cache->capacity] = NULL;
free(tail);
cache->size--;
}
cache->hashTable[node->key % cache->capacity] = node;
moveToHead(cache, node);
cache->size++;
}
// 获取缓存中的值
int get(struct LRUCache* cache, int key) {
struct CacheNode* node = cache->hashTable[key % cache->capacity];
if (node == NULL) return -1;
moveToHead(cache, node);
return node->value;
}
// 添加/更新缓存中的值
void put(struct LRUCache* cache, int key, int value) {
struct CacheNode* node = cache->hashTable[key % cache->capacity];
if (node != NULL) {
node->value = value;
moveToHead(cache, node);
} else {
struct CacheNode* newNode = createCacheNode(key, value);
addNode(cache, newNode);
}
}
2、处理大数相加
链表可以用来处理大数相加,每个节点存储一个数字位。
struct Node* addTwoNumbers(struct Node* l1, struct Node* l2) {
struct Node* dummyHead = createNode(0);
struct Node* p = l1;
struct Node* q = l2;
struct Node* current = dummyHead;
int carry = 0;
while (p != NULL || q != NULL) {
int x = (p != NULL) ? p->data : 0;
int y = (q != NULL) ? q->data : 0;
int sum = carry + x + y;
carry = sum / 10;
current->next = createNode(sum % 10);
current = current->next;
if (p != NULL) p = p->next;
if (q != NULL) q = q->next;
}
if (carry > 0) {
current->next = createNode(carry);
}
return dummyHead->next;
}
3、表达式求值
链表可以用来实现表达式求值,如中缀表达式转换为后缀表达式并求值。
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
// 定义堆栈节点结构
struct StackNode {
char data;
struct StackNode* next;
};
// 创建新堆栈节点
struct StackNode* createStackNode(char data) {
struct StackNode* newNode = (struct StackNode*)malloc(sizeof(struct StackNode));
newNode->data = data;
newNode->next = NULL;
return newNode;
}
// 判断堆栈是否为空
int isEmpty(struct StackNode* root) {
return !root;
}
// 入栈
void push(struct StackNode root, char data) {
struct StackNode* newNode = createStackNode(data);
newNode->next = *root;
*root = newNode;
}
// 出栈
char pop(struct StackNode root) {
if (isEmpty(*root)) return '