cover

Linked List 是面試必考、實務少用、但觀念影響深遠的資料結構。

先講結論

Linked List 的插入刪除只需要改指標,O(1) 搞定。代價是你沒辦法直接跳到第 i 個節點——得從頭走過去。跟 Array 比起來,它們的優缺點幾乎完全相反。

指標串起來的節點

每個節點存兩樣東西:資料本身,和指向下一個節點的指標。

head
  ↓
[10|→] → [20|→] → [30|→] → [40|→] → null

記憶體不連續,散落各處,靠指標串起來。這和 Array 的「一排連續格子」完全不同。

Array vs Linked List:一張表說完

ArrayLinked List
記憶體連續分散
隨機存取O(1)O(n) 從頭走
頭部插入O(n) 搬移O(1) 改指標
中間插入O(n) 搬移O(1) 改指標(但走到那裡要 O(n))
cache 友善度極高極低

最後一點很關鍵:現代 CPU 的 cache 機制偏愛連續記憶體。Linked List 的節點散落各處,每次走一步就可能 cache miss。這就是為什麼理論上 Linked List 在某些操作比較快,但實際跑起來 Array 常常贏——cache 效應太強了。

插入只改兩個指標

在 index 1 插入 15:

原本: [10] → [20] → [30]

Step 1: 新節點指向 [20]
        [15] → [20]

Step 2: [10] 改指向 [15]
        [10] → [15] → [20] → [30]

不用搬任何資料,只改兩個指標。這是 Linked List 最大的賣點。

刪除同理——把前一個節點跳過被刪的,指向下一個就好。被跳過的節點沒人引用,GC 會處理。

雙向鏈結串列:多花一點記憶體,換回便利

單向鏈結串列有個痛點:你只能往前走,不能往回。想刪最後一個節點?你得從頭走到倒數第二個。

雙向鏈結串列每個節點多存一個 prev 指標:

null ← [10] ⇄ [20] ⇄ [30] → null

removeLast 從 O(n) 變成 O(1)。Java 的 LinkedList 就是雙向實作。

環狀鏈結串列:尾巴接頭

[10] → [20] → [30] → [10] → ...(繞圈)

經典應用是 Round-Robin 排程——任務一個接一個輪流執行,跑完最後一個回到第一個。

老實說,什麼時候真的會用?

在大多數業務場景,你不太會直接用 Linked List。Array(動態陣列)在實務上幾乎全面碾壓。

但 Linked List 的觀念無處不在:

  • LRU Cache 用雙向鏈結串列維護使用順序
  • 作業系統的 process scheduling
  • React Fiber 的工作單元就是一種鏈表結構
  • Git 的 commit history 本質上也是

所以與其說「學 Linked List 要拿來用」,不如說「理解指標操作」本身就是程式設計的基本功。面試官考你 reverse linked list 不是在刁難你,是在看你會不會操作指標。好吧,也有一點在刁難你。


Linked List 就像手動排檔——你不會每天開,但懂了它你才真的理解變速箱怎麼運作的。