【演算法】LRU快取
1.概念介紹
假設快取的大小固定,初始狀態為空。每發生一次讀記憶體操作,首先查詢待讀取的資料是否
存在於快取中,如果存在則快取命中,返回資料,並將快取資料放到快取區頭部位置;否則快取未命中,返回提示資訊。
向快取新增資料時,如果快取已滿,則需要刪除訪問時間最早的資料,這種更新快取的方法就叫做LRU(Least Recently Used)。
2. 實際實現LRUCache類
基本要求如下
- LRUCache(int capacity) 用一個正整數表示的容量大小初始化快取空間。
- int get(int key) 如果對應的鍵存在於快取中,即命中返回鍵對應的值,未命中返回-1。
- void put(int key, int value) 如果已存在更新其內容。否則將鍵值對新增到快取中。 如果超過了最大容量,淡出最近最少使用的鍵值對。
同時要求get和put操作保證平均時間複雜度為O(1)。
案例介紹
-
示例輸入
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]] -
輸出
[null, null, null, 1, null, -1, null, -1, 3, 4] -
過程解釋
LRUCache lRUCache = new LRUCache(2);
LRUCache.put(1, 1); // 快取內容變為 {1=1}
LRUCache.put(2, 2); // 快取內容變為 {1=1, 2=2}
LRUCache.get(1); // 返回鍵為1的快取內容對應的值
LRUCache.put(3, 3); // LRU鍵為2, 新增到快取中前先移除鍵為2的快取內容然後新增新內容, 快取內容變為 {1=1, 3=3}
LRUCache.get(2); // 快取中不存在就返回-1
LRUCache.put(4, 4); // LRU鍵為1, 先移除鍵為1的快取內容然後新增新內容, 快取內容變為 {4=4, 3=3}
LRUCache.get(1); // 快取中不存在就返回-1
LRUCache.get(3); // 返回3
LRUCache.get(4); // 返回4
3.C++參考實現
class LRUCache { public: struct ListNode { int key; int val; ListNode *prev; ListNode *next; ListNode(): key(0), val(0), prev(nullptr), next(nullptr) {} ListNode(int _key, int _val): key(_key), val(_val), prev(nullptr), next(nullptr) {} }; public: LRUCache(const int capacity): cap(capacity) { head = new ListNode(-1, 0); tail = new ListNode(-1, 0); head->next = tail; tail->prev = head; } ~LRUCache(void) { if (cap>0) { ListNode *curNode = head->next; // 釋放連結串列中除首尾指示結點外所有結點佔用的資源 while (curNode != tail) { ListNode *tmpNode = curNode; curNode = curNode->next; delete tmpNode; tmpNode = nullptr; } // 清空雜湊表 mp.clear(); // 重置快取容量 cap = 0; } // 釋放首尾指標資源並將其設定為空指標 delete head; delete tail; head = nullptr; tail = nullptr; } int get(int key) { if (mp.find(key)==mp.end()) return -1; ListNode *curNode = mp[key]; // 如果已存在於當前快取中,將其提前到連結串列首部位置 moveToHead(curNode); return curNode->val; } void put(int key, int val) { if (mp.find(key)!=mp.end()) { ListNode *curNode = mp[key]; // 如果已存在於當前快取中,將其提前到連結串列首部位置 moveToHead(curNode); curNode->val = val; } else { if (cap>0 && mp.size()==cap) { // 刪除最後一個結點,因為時間最久沒被訪問 deleteNode(tail->prev); } // 在連結串列頭部新增新結點 ListNode *tmpNode = new ListNode(key, val); mp[key] = tmpNode; addNode(tmpNode); } } private: void moveToHead(ListNode *pnode) { // 斷開當前結點與前後結點的連線 curNode->prev->next = curNode->next; curNode->next->prev = curNode->prev; // 將結點連到連結串列頭部位置 curNode->next = head->next; head->next->prev = curNode; head->next = curNode; curNode->prev = head; } void deleteNode(ListNode *pnode) { pnode->prev->next = pnode->next; pnode->next->prev = pnode->prev; delete pnode; pnode = nullptr; } void addNode(ListNode *pnode) { pnode->next = head->next; head->next->prev = pnode; head->next = pnode; pnode->prev = head; } private: int cap; std::unordered_map<int, ListNode*> mp; ListNode *head; ListNode *tail; };
本文作者 :phillee
發表日期 :2022年03月07日
本文連結 :https://www.cnblogs.com/phillee/p/15975167.html
版權宣告 :自由轉載-非商用-非衍生-保持署名(創意共享3.0許可協議/CC BY-NC-SA 3.0)。轉載請註明出處!
限於本人水平,如果文章和程式碼有表述不當之處,還請不吝賜教。