單向鏈表是數據結構教學與工程實踐的基石。本文提供一個標準、完整、可編譯運行的C語言單向鏈表實現,涵蓋節點定義、鏈表創建、遍歷打印、清空銷毀、判空、求長度、按位置查找、插入、刪除等全部核心操作,代碼風格規範,邏輯清晰,適合教學參考與項目移植。
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct LinkNode {
int data; // 數據域(本例為int,可泛化為void*)
struct LinkNode* next; // 指針域
} LinkNode, *LinkList; // 別名:LinkList 等價於 LinkNode*
LinkList LinkCreate() {
LinkList head = NULL, tail = NULL;
int val;
while (1) {
scanf("%d", &val);
if (val == -1) break;
LinkNode* newNode = (LinkNode*)malloc(sizeof(LinkNode));
if (!newNode) { perror("malloc failed"); exit(EXIT_FAILURE); }
newNode->data = val;
newNode->next = NULL;
if (!head) {
head = tail = newNode;
} else {
tail->next = newNode;
tail = newNode;
}
}
return head; // 返回頭指針(無哨兵節點版本)
}
void LinkPrint(LinkList head) {
LinkNode* p = head;
while (p) {
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
void ClearList(LinkList head) {
LinkNode *curr = head, *next;
while (curr) {
next = curr->next;
free(curr);
curr = next;
}
// 注意:調用者需自行將頭指針置NULL
}
bool ListEmpty(LinkList head) {
return head == NULL;
}
int ListLength(LinkList head) {
int len = 0;
LinkNode* p = head;
while (p) {
len++;
p = p->next;
}
return len;
}
int GetElem(LinkList head, int pos) {
if (pos < 1 || pos > ListLength(head)) return -1; // 錯誤碼
LinkNode* p = head;
for (int i = 1; i < pos; i++) p = p->next;
return p->data;
}
bool ListInsert(LinkList* headRef, int val, int pos) {
int len = ListLength(*headRef);
if (pos < 1 || pos > len + 1) return false; // 允許在尾部插入(pos=len+1)
LinkNode* newNode = (LinkNode*)malloc(sizeof(LinkNode));
if (!newNode) return false;
newNode->data = val;
if (pos == 1) { // 頭插
newNode->next = *headRef;
*headRef = newNode;
return true;
}
// 找到第 pos-1 個節點
LinkNode* p = *headRef;
for (int i = 1; i < pos - 1; i++) p = p->next;
newNode->next = p->next;
p->next = newNode;
return true;
}
bool ListDelete(LinkList* headRef, int pos) {
if (pos < 1 || pos > ListLength(*headRef)) return false;
LinkNode* delNode;
if (pos == 1) { // 刪除頭節點
delNode = *headRef;
*headRef = delNode->next;
} else {
LinkNode* p = *headRef;
for (int i = 1; i < pos - 1; i++) p = p->next;
delNode = p->next;
p->next = delNode->next;
}
free(delNode);
return true;
}
int main() {
printf("請輸入鏈表數據(-1結束):");
LinkList list = LinkCreate();
printf("創建完成:"); LinkPrint(list);
printf("長度:%d\n", ListLength(list));
printf("第3個元素:%d\n", GetElem(list, 3));
ListInsert(&list, 99, 2);
printf("在位置2插入99:"); LinkPrint(list);
ListDelete(&list, 1);
printf("刪除頭節點:"); LinkPrint(list);
ClearList(list);
printf("銷毀後判空:%s\n", ListEmpty(list) ? "真" : "假");
return 0;
}
| 操作 | 時間複雜度 | 關鍵難點 | 邊界處理 |
|---|---|---|---|
| 創建 | O(n) | 尾指針維護 | 空表、單節點 |
| 遍歷 | O(n) | 指針推進 | 空表 |
| 查找 | O(n) | 索引轉指針 | 越界返回錯誤碼 |
| 插入 | O(n) | 前驅節點定位、頭插特判 | pos=1, pos=len+1 |
| 刪除 | O(n) | 前驅節點定位、釋放內存、頭刪特判 | pos=1, pos=len |
| 銷毀 | O(n) | 釋放前保存next | 調用者置NULL |
LinkList*(二級指針)void* data + 銷毀函數指針 + 比較函數指針prev 指針,O(1) 尾部操作、反向遍歷本文提供的單向鏈表實現代碼結構清晰、邊界處理完善、內存管理規範,涵蓋了數據結構教學大綱要求的全部基本操作。掌握本文代碼模式,即可靈活應對考試填空、面試手寫、項目開發中的鏈表相關需求。建議讀者在理解基礎上,嘗試實現帶哨兵節點版本、泛型版本及雙向鏈表,深化指針操作功力。