循環單向鏈表將尾節點的 next 指向頭節點,形成閉環。這一特性帶來獨特優勢:可從任意節點出發遍歷全表,無須固定頭指標,極大提升操作靈活性。本文詳細解析其與普通單向鏈表的差異,並給出關鍵操作的完整 C 實作。
普通鏈表以 NULL 為終點;循環鏈表則以 回到起始節點 為終止條件。這影響所有遍歷類操作(列印、查找、統計等)。
void print_data(const LINK_NODE* pHead) {
if (!pHead) return;
const LINK_NODE* pCur = pHead;
do {
printf("%d\n", pCur->data);
pCur = pCur->next;
} while (pCur != pHead); // 關鍵:回到頭節點即結束
}
插入邏輯分兩種情況:
next 指向自己,成為頭節點STATUS insert_data(LINK_NODE** ppHead, int data) {
if (!ppHead) return FALSE;
LINK_NODE* pNew = create_link_node(data);
assert(pNew);
if (!*ppHead) { // 空表
pNew->next = pNew;
*ppHead = pNew;
return TRUE;
}
if (find_data(*ppHead, data)) return FALSE; // 唯一性檢查
pNew->next = (*ppHead)->next;
(*ppHead)->next = pNew;
return TRUE;
}
刪除需找到目標節點的前驅節點。循環特性允許從目標節點出發繞一圈找到前驅:
STATUS delete_data(LINK_NODE** ppHead, int data) {
if (!ppHead || !*ppHead) return FALSE;
LINK_NODE* pTarget = find_data(*ppHead, data);
if (!pTarget) return FALSE;
LINK_NODE* pPrev = pTarget->next;
while (pPrev->next != pTarget) // 繞圈找前驅
pPrev = pPrev->next;
if (pTarget == *ppHead) { // 刪除頭節點
if (pTarget->next == pTarget) { // 唯一節點
*ppHead = NULL;
} else {
pPrev->next = pTarget->next;
*ppHead = pTarget->next; // 更新頭指標
}
} else {
pPrev->next = pTarget->next; // 一般節點
}
free(pTarget);
return TRUE;
}
pCur != pHead 條件循環單向鏈表雖結構簡單,但其「無固定起點」特性在循環緩衝區、輪詢調度、Josephus 問題等場景極具價值。掌握其操作細節,是進階資料結構應用的基石。