本實現採用「頭文件聲明 + 源文件實現」的經典模塊化結構,將數據結構定義、接口聲明與具體算法分離,便於維護與復用。核心數據類型抽象為 datatype(默認 int),通過宏切換可適配不同業務場景。
// function.h
#define true 1
#define false 0
typedef int datatype; // 業務數據類型抽象層
// 單鏈表節點:數據域 + 指針域
typedef struct LNode {
datatype data;
struct LNode *next;
} LNode, *LinkList; // LinkList 本質是指向頭節點的指針
採用帶頭節點設計:頭節點不存儲有效數據,next 指向首個數據節點。這統一了空表、插入刪除頭節點等邊界條件處理,代碼更簡潔健壯。
| 接口 | 功能 | 關鍵參數 |
|---|---|---|
GetElem | 按位序查找元素 | head, i, *e |
ListInsert | 指定位置前插入 | head, i, e |
ListDelete | 刪除指定位置元素 | head, i, *e |
CreateList | 頭插法建表 | head, n |
MergeList | 合併兩有序表 | headA, headB, headC |
ListEmpty/Length | 判空/求長 | head |
ListLocate/Pos | 按位序/值查找節點 | head, k |
ListPrint/Destroy | 遍歷輸出/銷毀釋放 | head |
插入、刪除、查找均基於「定位第 i-1 個節點」:
LinkList p = head; // 從頭節點開始
int j = 0;
while (p && j < i-1) { p = p->next; j++; }
if (!p || j > i-1) return false; // 位置非法
// p 現在指向第 i-1 個節點,為操作目標節點的前驅
此模式將「找節點」與「做操作」解耦,邏輯清晰,邊界處理統一。
LinkList newNode = (LinkList)malloc(sizeof(LNode)); newNode->data = e; // 1. 填充數據 newNode->next = p->next; // 2. 新節點指向後繼 p->next = newNode; // 3. 前驅指向新節點
順序不可顛倒,否則會丟失後續鏈表。
LinkList q = p->next; // 待刪節點 *e = q->data; // 保存數據 p->next = q->next; // 斷鏈 free(q); // 釋放內存
void MergeList(LinkList A, LinkList B, LinkList C) {
C = A; // 復用 A 的頭節點作為合併表頭
LinkList pa = A->next, pb = B->next, pc = C;
while (pa && pb) {
if (pa->data <= pb->data) {
pc->next = pa; pc = pa; pa = pa->next;
} else {
pc->next = pb; pc = pb; pb = pb->next;
}
}
pc->next = pa ? pa : pb; // 掛載剩餘段
free(B); // 釋放 B 的頭節點
}
時間複雜度 O(m+n),空間複雜度 O(1),就地合併,極致高效。
void ListDestroy(LinkList head) {
while (head) {
LinkList p = head;
head = head->next;
free(p);
}
}
注意:頭節點也需釋放,調用後原頭指針成為懸空指針,上層應置 NULL。
typedef int datatype 便於後期擴展為 float、結構體等true/false,避免魔術數字ListPrint、GetElem)參數應加 const LinkListmalloc 失敗檢查、釋放後置空、防止重複釋放ListPrint 格式化輸出,便於單元測試驗證prev 指針,支持反向遍歷、O(1) 刪除已知節點void* + 類型標識 + 操作函數指針,實現真正泛型容器這份實現雖屬教學級,但體現了C語言數據結構工程化的核心原則:抽象封裝、邊界統一、內存規範、接口正交。掌握此模式,可快速構建生產級容器組件。