【LeetCode】146. LRU 快取機制
阿新 • • 發佈:2021-09-04
題目描述
運用你所掌握的資料結構,設計和實現一個 LRU (最近最少使用) 快取機制 。
實現 LRUCache 類:
- LRUCache(int capacity) 以正整數作為容量capacity 初始化 LRU 快取
- int get(int key) 如果關鍵字 key 存在於快取中,則返回關鍵字的值,否則返回 -1 。
- void put(int key, int value)如果關鍵字已經存在,則變更其資料值;如果關鍵字不存在,則插入該組「關鍵字-值」。當快取容量達到上限時,它應該在寫入新資料之前刪除最久未使用的資料值,從而為新的資料值留出空間。
進階:你是否可以在O(1) 時間複雜度內完成這兩種操作?
方法一:雜湊表 + 雙向連結串列
LRU(Least Recently Used),最近最少使用演算法,是一種常用的頁面置換演算法,選擇最近最久未使用的頁面予以淘汰。
使用雜湊表來實現 O(1) 時間複雜度的 get,再輔以雙向連結串列實現 O(1) 時間複雜的 put,同時雙向連結串列還實現了對資料按照訪問時間排序。具體來說:
- 雙向連結串列結點包含對 key,value 的封裝
- 雜湊表以資料的 <key,DLinkedNode> 為對映,通過 key 對映到其在雙向連結串列中的結點。
// 雙向連結串列 struct DLinkedNode { int key; int value; DLinkedNode *prev; DLinkedNode *next; DLinkedNode() : key(0), value(0), prev(nullptr), next(nullptr){}; DLinkedNode(int key, int value) : key(key), value(value), prev(nullptr), next(nullptr){}; }; class LRUCache { private: unordered_map<int, DLinkedNode *> cache; DLinkedNode *head; // 雙向連結串列頭指標 DLinkedNode *tail; // 雙向連結串列尾指標 int capacity; // cache 容量 int size; // 雙向連結串列的大小 public: LRUCache(int capacity) : capacity(capacity), size(0) { // 初始化的時候需要先建立頭尾結點,並且頭尾指標要構成鏈 head = new DLinkedNode(); tail = new DLinkedNode(); head->next = tail; tail->prev = head; } /* get 操作,判斷 key 是否存在 1. 不存在,返回 -1 2. 存在,將 key 對應的結點移至雙向連結串列表頭,返回該結點的值 */ int get(int key) { if (!cache.count(key)) return -1; DLinkedNode *node = cache[key]; moveToHead(node); return node->value; } /* put 操作,判斷 key 是否存在 1. 不存在 1)建立 key,value 的新結點,將此 <key,DLinkedNode> 加入到雜湊表對映中。 2) 判斷快取是否已滿,若滿了,需要刪除最後一個結點,並在雜湊表中刪除對映。 3)在雙向連結串列頭插入此結點。 2. 存在 1) 將 key 對應結點移至表頭 2) 更新結點的 value 值 */ void put(int key, int value) { if (!cache.count(key)) // 不存在 { DLinkedNode *node = new DLinkedNode(key, value); cache[key] = node; if (capacity == size) { DLinkedNode *node = removeTail(); cache.erase(node->key); delete node; } addToHead(node); } else { DLinkedNode *node = cache[key]; node->value = value; moveToHead(node); } } void moveToHead(DLinkedNode *node) { // 當前節點脫鏈 node->prev->next = node->next; node->next->prev = node->prev; size--; addToHead(node); } // 將尾結點從連結串列脫離,並返回尾結點 DLinkedNode *removeTail() { DLinkedNode *node = tail->prev; node->prev->next = tail; tail->prev = node->prev; size--; return node; } void addToHead(DLinkedNode *node) { // 將當前節點移至連結串列頭 node->next = head->next; node->prev = head; head->next->prev = node; head->next = node; size++; } }; int main() { // 驗證 LRUCache my_lru(2); my_lru.put(1, 1); my_lru.put(2, 2); my_lru.get(1); my_lru.put(3, 3); my_lru.get(2); my_lru.put(4, 4); my_lru.get(1); my_lru.get(3); my_lru.get(4); }