← 回首頁

鏈結串列與 LRU Cache(單向鏈結 / HashMap + 雙向鏈結)

想像一排火車車廂,每節只記得「下一節掛在哪」——這就是鏈結串列。它和陣列剛好互補:陣列像劃好位子的座位表,要在最前面塞一個人得整排往後挪;鏈結串列想插隊只要改一下「掛勾」就好,但要找第幾節就得從頭一節一節數過去。LRU Cache 則像容量有限的書桌,最近用的東西擺到手邊、桌子滿了就先收走最久沒碰的那樣。下面你會看到這兩種結構如何一步步改指標、把常用的東西留在手邊。

鏈結串列與動態陣列的取捨相反:陣列用「連續記憶體 + 索引」換來任意位置 O(1) 隨機存取,但頭部插入/刪除要整批搬移; 鏈結串列放棄隨機存取(找節點需沿指標走訪 O(n)),換來頭部插入/刪除只需改指標的 O(1)。 LRU Cache 則把兩種結構的長處疊在一起:HashMap 提供 O(1) 鍵定位,雙向鏈結提供 O(1) 的「移到最近使用端」與「淘汰最久未使用端」,兩者缺一都做不到整體 O(1)。

1. 單向鏈結串列(Singly Linked List)

每個節點只存 value 與指向下一個節點的 next 指標;addFirst 只需改 head 指標為 O(1),addLast/insert/remove 都要先沿 next 走訪到目標位置的前一個節點才能改指標,故為 O(n)。reverse 逐節點把 next 反指向前一個節點(prev/curr/next 三指標迭代), 走訪中尚未換 head 前,被反轉的節點會暫時從 head 端不可達 —— 下方另闢一列如實呈現這段「已反轉部分」。

固定示範腳本:addLast 5 → addLast 8 → addFirst 3 → insert(2,9) → remove(1) → reverse。終態(獨立模擬,head→tail):[8, 9, 3]。

操作腳本

    指標操作次數:0
    節點盒顯示 value,方框內編號為節點 id;紅框(current)為目前操作/走訪中的節點,橘框(changed)為本步驟指標被改動的節點(同一節點兩者皆符合時紅框優先顯示);chain 恆為「從 head 沿 next 可達」的序列。

    指標面板

    size0

    已執行操作紀錄

    2. LRU Cache(HashMap + 雙向鏈結,容量 3)

    每個節點存 key/value,並以 head/tail 虛擬節點串成雙向鏈結:get 命中或 put 都會把節點 moveToHead 移到 MRU 端; put 新鍵若超過容量,淘汰 tail 端(LRU 端)的節點並自 hash 表移除。hash 表只負責「這個 key 對應哪個節點」的 O(1) 定位, 實際的「誰最近用過」順序完全交給雙向鏈結維護,兩者缺一都無法同時做到 O(1) 存取與 O(1) 淘汰。

    固定示範腳本(容量 3):put(1,10) put(2,20) put(3,30) get(1) put(4,40)[淘汰 2] get(2)[miss] put(3,99)[更新] get(4)。終態(獨立模擬,LRU→MRU):[{1,10},{3,99},{4,40}];hits=2、misses=1、淘汰 key=2。

    操作腳本

      操作次數:0

      hash 表當前鍵集

      節點盒顯示 key:value,⇄ 表示雙向鏈結,左端 LRU(最久未使用)、右端 MRU(最近使用);hash 鍵格為 hash 表目前持有的 key。紅框(current)標示本步驟操作的 key;淘汰發生時,該節點已於同一幀從鏈結與 hash 表移除,改以虛線淘汰標記呈現。

      指標面板

      capacity0
      hits0
      misses0

      已執行操作紀錄