LRU原理及其实现(C++)
LRU原理
在一般標準的操作系統教材里,會用下面的方式來演示 LRU 原理,假設內存只能容納3個頁大小,按照 7 0 1 2 0 3 0 4 的次序訪問頁。假設內存按照棧的方式來描述訪問時間,在上面的,是最近訪問的,在下面的是,最遠時間訪問的,LRU就是這樣工作的。
但是如果讓我們自己設計一個基于 LRU 的緩存,這樣設計可能問題很多,這段內存按照訪問時間進行了排序,會有大量的內存拷貝操作,所以性能肯定是不能接受的。
那么如何設計一個LRU緩存,使得放入和移除都是 O(1) 的,我們需要把訪問次序維護起來,但是不能通過內存中的真實排序來反應,有一種方案就是使用雙向鏈表。
?
基于 HashMap 和 雙向鏈表實現 LRU
整體的設計思路是,可以使用 HashMap 存儲 key,這樣可以做到 save 和 get key的時間都是 O(1),而 HashMap 的 Value 指向雙向鏈表實現的 LRU 的 Node 節點。
LRU 存儲是基于雙向鏈表實現的,下面的圖演示了它的原理。其中 head 代表雙向鏈表的表頭,tail 代表尾部。首先預先設置 LRU 的容量,如果存儲滿了,可以通過 O(1) 的時間淘汰掉雙向鏈表的尾部,每次新增和訪問數據,都可以通過 O(1)的效率把新的節點增加到對頭,或者把已經存在的節點移動到隊頭。
下面展示了,預設大小是 3 的,LRU存儲的在存儲和訪問過程中的變化。為了簡化圖復雜度,圖中沒有展示 HashMap部分的變化,僅僅演示了上圖 LRU 雙向鏈表的變化。我們對這個LRU緩存的操作序列如下:
?
相應的 LRU 雙向鏈表部分變化如下:
總結一下核心操作的步驟:
save(key, value),首先在 HashMap 找到 Key 對應的節點,如果節點存在,更新節點的值,并把這個節點移動隊頭。如果不存在,需要構造新的節點,并且嘗試把節點塞到隊頭,如果LRU空間不足,則通過 tail 淘汰掉隊尾的節點,同時在 HashMap 中移除 Key。
get(key),通過 HashMap 找到 LRU 鏈表節點,因為根據LRU 原理,這個節點是最新訪問的,所以要把節點插入到隊頭,然后返回緩存的值。
?
完整基于 C++的代碼參考如下
#include <iostream> #include <map> using namespace std;struct DLinkList {int key, val;DLinkList *pre;DLinkList *next;DLinkList(): key(0), val(0), pre(NULL), next(NULL){}DLinkList(int _key, int _val): key(_key), val(_val), pre(NULL), next(NULL){} };class LRUCache { private:map<int, DLinkList *> maap;DLinkList *head;DLinkList *tail;int size;int cap;public:LRUCache(int capicity){head = new DLinkList();tail = new DLinkList();head->next = tail;tail->pre = head;size = 0;cap = capicity;}int Get(int key){if (!maap.count(key)){return -1;}DLinkList *node = maap[key];MoveToHead(node);return node->val;}void MoveToHead(DLinkList *node){ReMoveNode(node);AddToHead(node);}//從雙鏈表中刪除void ReMoveNode(DLinkList* node){node->pre->next=node->next;node->next->pre=node->pre;}void AddToHead(DLinkList* node){node->next=head->next;node->pre=head;head->next->pre=node;head->next=node;}void Save(int key,int value){if(maap.count(key)){DLinkList* node=maap[key];MoveToHead(node);node->val=value;}else{DLinkList* node=new DLinkList(key,value);AddToHead(node);maap[key]=node;size++;if(size>cap){DLinkList* delNode=ReMoveTail();maap.erase(delNode->key);delete delNode;size--;}}}DLinkList* ReMoveTail(){DLinkList* node=tail->pre;ReMoveNode(node);return node;} };?
總結
以上是生活随笔為你收集整理的LRU原理及其实现(C++)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 编程之美读书笔记2.1—求二进制数中1的
- 下一篇: map,multimap,unorder