鏈表因非連續記憶體、無隨機訪問特性,排序演算法選型與陣列大異。本文分析八大經典排序在鏈表上的適用性,並給出插入排序完整實現。
| 演算法 | 適用性 | 理由 |
|---|---|---|
| 插入排序 | ✅ 最適合 | 僅需順序遍歷、指針調整,無隨機訪問 |
| 冒泡排序 | ✅ 適合 | 相鄰節點交換指針即可 |
| 希爾排序 | ✅ 適合 | 可通過步長跳躍指針實現 |
| 選擇排序 | ✅ 適合 | 遍歷找極值,交換節點數據或指針 |
| 快速排序 | ❌ 不適合 | 需隨機訪問基準值、雙向游標,鏈表極難高效實現 |
| 歸併排序 | ❌ 不適合 | 雖可遞歸分治,但分割鏈表需遍歷中點,常數極大 |
| 基數排序 | ❌ 不適合 | 需桶陣列隨機訪問,鏈表不具備 |
| 堆排序 | ❌ 不適合 | 堆結構依賴隨機訪問下標計算父子節點 |
核心原則:僅涉及數據交換的排序適合鏈表;需中間值、隨機訪問、下標運算的不適合。
typedef struct Node {
int data;
struct Node* next;
} NODE;
// 將 pNode 插入以 ppHead 為頭的有序鏈表中
void insertSorted(NODE** ppHead, NODE* pNode) {
pNode->next = NULL; // 斷開原鏈接
// 插入頭部
if (!*ppHead || pNode->data < (*ppHead)->data) {
pNode->next = *ppHead;
*ppHead = pNode;
return;
}
// 尋找插入位置
NODE* cur = *ppHead;
while (cur->next && pNode->data >= cur->next->data)
cur = cur->next;
pNode->next = cur->next;
cur->next = pNode;
}
// 主排序函數:原地插入排序
void sortList(NODE** ppHead) {
if (!ppHead || !*ppHead) return;
NODE* sorted = *ppHead; // 已排序部分頭
NODE* curr = sorted->next; // 待排序部分頭
sorted->next = NULL; // 斷開
while (curr) {
NODE* next = curr->next; // 保存下一個
insertSorted(&sorted, curr);
curr = next;
}
*ppHead = sorted;
}
NODE** 允許修改頭指針(插入新最小值時)。insertSorted 前保存 curr->next,因插入過程會修改 curr->next。>= 判斷插入位置,保證相等元素相對順序不變(穩定排序)。若鏈表長度 > 1000,建議先轉陣列(或 vector)用 O(n log n) 排序,再重建鏈表;或實現鏈表版歸併排序(遞歸分割 + 合併),雖代碼較長但穩定 O(n log n)。