單鏈表是資料結構的基石,掌握其操作對理解指標、動態記憶體管理至關重要。本文提供一個功能完整、模組化設計的單鏈表實作,涵蓋基礎增刪查改、進階演算法(逆置、合併、尋找中點/倒數第 k 節點)與測試範例。
typedef int DataType;
typedef struct SListNode {
DataType data;
struct SListNode* next;
} SListNode;
| 函數 | 功能 | 備註 |
|---|---|---|
PushBack | 尾插 | O(n),支援空鏈表 |
PopBack | 尾刪 | 處理空/單節點/多節點三種情況 |
PushFront | 頭插 | O(1) |
PopFront | 頭刪 | O(1) |
Insert | 指定節點後插入 | 需傳入前驅節點指標 |
Erase | 刪除指定節點 | 需頭指標引用以處理刪除頭節點 |
Find | 按值查找 | 返回首個匹配節點指標 |
Reverse | 鏈表逆置 | 就地逆置,O(n) 時間,O(1) 空間 |
FindMidNode | 尋找中間節點 | 快慢指標法,單次遍歷 |
FindKNode | 尋找倒數第 k 個節點 | 快慢指標法,單次遍歷 |
Merge | 合併兩有序鏈表 | 遞迴實作,合併後保持有序 |
Sort | 冒泡排序 | 原地交換 data,適合教學 |
PrintTailToHead | 反向列印 | 遞迴實作 |
void Reverse(SListNode*& pHead) {
SListNode* cur = pHead;
SListNode* newHead = NULL;
while (cur) {
SListNode* tmp = cur;
cur = cur->next;
tmp->next = newHead;
newHead = tmp;
}
pHead = newHead;
}
核心思想:逐個摘下節點,插入新鏈表頭部。三指標法(cur, newHead, tmp)清晰易懂。
// 中間節點:快指標走兩步,慢指標走一步
SListNode* FindMidNode(SListNode* pHead) {
SListNode *fast = pHead, *slow = pHead;
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
// 倒數第 k 個:快指標先走 k 步,再一起走
SListNode* FindKNode(SListNode* pHead, int k) {
SListNode *fast = pHead, *slow = pHead;
while (fast && k--) fast = fast->next;
if (k > 0) return NULL; // k 超過鏈表長度
while (fast) {
slow = slow->next;
fast = fast->next;
}
return slow;
}
SListNode* Merge(SListNode* pHead1, SListNode* pHead2) {
if (!pHead1) return pHead2;
if (!pHead2) return pHead1;
SListNode* merged = NULL;
if (pHead1->data < pHead2->data) {
merged = pHead1;
merged->next = Merge(pHead1->next, pHead2);
} else {
merged = pHead2;
merged->next = Merge(pHead1, pHead2->next);
}
return merged;
}
遞迴版本簡潔優雅,但注意棧溢出風險;長鏈表建議用迭代版。
提供四組測試函數:
Test1:基礎增刪操作驗證Test2:查找、插入、刪除指定位置Test3:進階功能(中點、倒數第 k、反向列印、逆置)Test4:排序與合併有序鏈表DestoryList 必須逐節點 free,並將頭指標設 NULLfree 後立即將指標設 NULL,或使用封裝的 sh_freeSListNode* &,僅遍歷用 SListNode*SList.h // 介面宣告
SList.c // 核心實作
Test.c // 測試用例
main() // 選擇性執行測試
建議將 SList.h/.c 編譯為靜態庫,專案中直接連結,實現真正的模組化複用。