題目描述:設計並實作一個 LRU(Least Recently Used,最近最少使用)快取機制,支援以下操作:
LRUCache(int capacity):以指定容量初始化 LRU 快取。int get(int key):若 key 存在,返回對應的值,否則返回 -1。void put(int key, int value):插入或更新 key 的值。若容量已滿,在插入新資料前,必須先移除最久未使用的 key。要求:get 和 put 操作均須在 O(1) 時間內完成。
解題思路:要同時達成 O(1) 的查找、O(1) 的插入/刪除以及維護使用順序,需要結合兩種資料結構:
核心設計:
get:在哈希表中找到節點後,將其移至串列頭部(表示最近使用),返回其值。put:若 key 已存在,更新值並移至頭部;若 key 不存在,建立新節點插入頭部。若容量超限,刪除串列尾部的節點(最久未使用)並從哈希表中移除對應 key。時間複雜度:O(1) — get 操作透過哈希表直接找到節點(O(1)),再執行固定次數的指標操作將節點移至頭部(O(1))。put 操作同樣透過哈希表進行查找、插入或刪除,所有指標操作均為常數時間。雙向鏈結串列的任意節點刪除不需線性掃描,這是比單向鏈結串列更適合此場景的關鍵原因。
空間複雜度:O(capacity) — 哈希表和雙向鏈結串列各自最多儲存 capacity 個元素,因此空間使用量與快取容量成線性關係。兩個虛擬節點(dummy head 和 dummy tail)僅佔用常數空間。
1. Initialize: create dummy head and dummy tail nodes, link them together. Create empty hashmap. Store capacity. 2. Helper removeNode(node): unlink node from its neighbors (node.prev.next = node.next, node.next.prev = node.prev). 3. Helper insertFront(node): insert node between dummy head and current first node. 4. get(key): a. If key not in hashmap, return -1. b. Otherwise, retrieve node from hashmap. c. removeNode(node), insertFront(node) — mark as recently used. d. Return node.val. 5. put(key, value): a. If key exists in hashmap: update node.val, removeNode, insertFront. b. Else if cache is full: get LRU node = tail.prev, removeNode(LRU), erase from hashmap, free memory. c. Create new node(key, value), insertFront(newNode), add to hashmap.
使用 C++ std::list + unordered_map O(1):利用 C++ 標準庫的雙向鏈結串列 std::list 搭配迭代器,可以避免手動管理節點記憶體。哈希表儲存 key 到 list::iterator 的映射,透過 splice 操作在 O(1) 內將節點移至串列頭部。此方法功能與手寫雙向串列相同,但更簡潔,適合在實際工程中使用。
使用 Python OrderedDict(僅供概念參考):在 Python 中,collections.OrderedDict 內建了維護插入順序的能力,並提供 move_to_end() 方法,使 LRU Cache 的實作極為簡潔(約 10 行)。雖然不適用於 C++ 解題,但理解其底層原理有助於加深對哈希表 + 雙向鏈結串列組合的理解。