單鏈表是數據結構教學與工程實踐的基石。本文提供一個模塊化、可編譯、含測試主程序的帶頭節點單鏈表 C 實現,涵蓋逆序建表、指定位置插入/刪除、按位查找、兩有序鏈表歸併,並詳細講解二級指針參數傳遞、內存管理、邊界處理等關鍵工程細節。
declaration.h // 類型定義、狀態碼
function.h // 接口聲明
function.c // 核心實現
main.c // 測試驗證
// declaration.h
#ifndef DECLARATION_H
#define DECLARATION_H
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
typedef int ElemType;
typedef int Status;
typedef struct LNode {
ElemType data;
struct LNode *next;
} LNode, *LinkList; // LinkList 本質是指向頭節點的指針
#endif
// function.h #ifndef FUNCTION_H #define FUNCTION_H void CreateList_L(LinkList *L, int n); // 逆序輸入 n 個元素建表 Status ListInsert_L(LinkList *L, int i, ElemType e); // 第 i 位前插入 Status ListDelete_L(LinkList *L, int i, ElemType *e); // 刪除第 i 位,值回傳 e Status GetElem_L(LinkList L, int i, ElemType *e); // 查找第 i 位 Status MergeList_L(LinkList *La, LinkList *Lb, LinkList *Lc); // 歸併有序表 void PrintList_L(LinkList L); // 打印鏈表 #endif
void CreateList_L(LinkList *L, int n) {
*L = (LinkList)malloc(sizeof(LNode)); // 分配頭節點
(*L)->next = NULL;
for (int i = n; i > 0; --i) { // 逆序輸入 → 正序鏈表
LinkList p = (LinkList)malloc(sizeof(LNode));
scanf("%d", &p->data);
p->next = (*L)->next; // 頭插法
(*L)->next = p;
}
}
關鍵點:LinkList *L 是二級指針,*L 才是頭指針。調用時傳 &La,函數內 *L = malloc(...) 修改的是主函數的 La 變量本身。
Status ListInsert_L(LinkList *L, int i, ElemType e) {
LinkList p = *L; int j = 0;
while (p && j < i-1) { p = p->next; ++j; } // 尋找第 i-1 個節點
if (!p || j > i-1) return ERROR; // 位置非法
LinkList s = (LinkList)malloc(sizeof(LNode));
s->data = e;
s->next = p->next; // ① 先連後繼
p->next = s; // ② 再連前驅
return OK;
}
Status ListDelete_L(LinkList *L, int i, ElemType *e) {
LinkList p = *L; int j = 0;
while (p->next && j < i-1) { p = p->next; ++j; }
if (!p->next || j > i-1) return ERROR;
LinkList q = p->next;
p->next = q->next; // 斷開 q
*e = q->data;
free(q);
return OK;
}
Status MergeList_L(LinkList *La, LinkList *Lb, LinkList *Lc) {
LinkList pa = (*La)->next, pb = (*Lb)->next;
*Lc = (LinkList)malloc(sizeof(LNode)); // Lc 頭節點
LinkList pc = *Lc;
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; // 連接剩餘段
// 可選:釋放 La, Lb 頭節點,置空原表
return OK;
}
// main.c
int main() {
LinkList La, Lb, Lc;
printf("Input 5 numbers for La (reverse order stored): ");
CreateList_L(&La, 5);
printf("La: "); PrintList_L(La);
ElemType e;
GetElem_L(La, 3, &e);
printf("3rd element: %d\n", e);
ListDelete_L(&La, 1, &e);
printf("Deleted 1st: %d, La now: ", e); PrintList_L(La);
printf("Input 4 numbers for Lb: ");
CreateList_L(&Lb, 4);
printf("Lb: "); PrintList_L(Lb);
ListInsert_L(&Lb, 2, 3);
printf("After insert 3 at pos 2, Lb: "); PrintList_L(Lb);
if (MergeList_L(&La, &Lb, &Lc) == OK) {
printf("Merged Lc: "); PrintList_L(Lc);
}
return 0;
}
Input 5 numbers for La: 5 4 3 2 1
La: 1 2 3 4 5
3rd element: 3
Deleted 1st: 1, La now: 2 3 4 5
Input 4 numbers for Lb: 10 20 30 40
Lb: 40 30 20 10
After insert 3 at pos 2, Lb: 40 3 30 20 10
Merged Lc: 2 3 4 5 10 20 30 40
LinkList*;僅遍歷/修改節點用 LinkListdata 無意義,簡化邊界條件La/Lb 節點已被 Lc 接管,勿再釋放;僅釋放兩個頭節點scanf 返回值檢查、緩衝區溢出防護 (生產代碼用 fgets+sscanf)void list_foreach(LinkList, void(*)(ElemType)) 解耦遍歷邏輯本實現展示了 C 語言手動內存管理下鏈表操作的標準範式:二級指針傳遞頭指針所有權、頭節點統一邊界、原地指針重組避免數據拷貝。歸併算法更體現了鏈表「指針重鏈接」優於數組「元素搬移」的核心優勢。掌握此代碼庫,可直接應用於嵌入式內存池管理、編譯器符號表、服務器連接鏈表等實時系統模塊。