題目:
Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get
and set
.
get(key)
- Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1. set(key, value)
- Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.
解題思路:
利用雙向連結串列+hashtable實現
Cache中的儲存空間往往是有限的,當Cache中的儲存塊被用完,而需要把新的資料Load進Cache的時候,我們就需要設計一種良好的演算法來完成資料塊的替換。LRU的思想是基於“最近用到的資料被重用的概率比較早用到的大的多”這個設計規則來實現的。
為了能夠快速刪除最久沒有訪問的資料項和插入最新的資料項,我們雙向連結串列連線Cache中的資料項,並且保證連結串列維持資料項從最近訪問到最舊訪問的順序。每次資料項被查詢到時,都將此資料項移動到連結串列頭部(O(1)的時間複雜度)。這樣,在進行過多次查詢操作後,最近被使用過的內容就向連結串列的頭移動,而沒有被使用的內容就向連結串列的後面移動。當需要替換時,連結串列最後的位置就是最近最少被使用的資料項,我們只需要將最新的資料項放在連結串列頭部,當Cache滿時,淘汰連結串列最後的位置就是了。
查詢一個連結串列中元素的時間複雜度是O(n),每次命中的時候,我們就需要花費O(n)的時間來進行查詢,怎麼樣才能提高查詢的效率呢?當然是hashtable了,因為它的查詢時間複雜度是O(1)。
實現程式碼:
#include <iostream> #include <unordered_map> using namespace std; /* Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and set. get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1. set(key, value) - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item. */ //雙向連結串列節點 struct LRUNode { int key; int val; LRUNode *pre; LRUNode *next; LRUNode(int k = 0, int v = 0):key(k), val(v),pre(NULL), next(NULL){ } }; class LRUCache{ public: LRUCache(int capacity):cap(capacity),size(0) { head = new LRUNode(); tail = new LRUNode(); head->next = tail; tail->pre = head; } int get(int key) { LRUNode *t = hashtable[key]; if(t)//key在hashtable存在,則調整該key在連結串列中對應節點的位置,將其插入到最前面 { //分離t節點 t->pre->next = t->next; t->next->pre = t->pre; //將t節點插入到頭結點之後,即第一個資料節點 t->pre = head; t->next = head->next; head->next = t; t->next->pre = t; return t->val; } else return -1; } void set(int key, int value) { LRUNode *t = hashtable[key]; if(t)//key在hashtable存在,則更新value及調整該key對應節點在連結串列中位置,將其插入到第一個節點 { t->val = value; //分離t節點 t->pre->next = t->next; t->next->pre = t->pre; //將t節點插入到頭結點之後,即第一個資料節點 t->pre = head; t->next = head->next; head->next = t; t->next->pre = t; return ; } if(size == cap)//如果雙向連結串列容量已滿即快取容量已滿,則將最近不使用的節點即連結串列最後一個資料節點刪除 { LRUNode *tmp = tail->pre; tail->pre->pre->next = tail; tail->pre = tmp->pre; hashtable.erase(tmp->key); delete tmp; size--; } //建立key對應的一個新節點,插入到最前面 LRUNode *node = new LRUNode(key, value); node->pre = head; node->next = head->next; head->next = node; node->next->pre = node; hashtable[key] = node;//在hashtable新增key對應的表項 size++;//連結串列節點數++ } private: int cap;//連結串列容量即快取容量 int size;//快取當前使用量 LRUNode *head;//連結串列頭結點,不存資料, LRUNode *tail;//連結串列尾節點,不存資料 unordered_map<int,LRUNode*> hashtable;//hashtable,用作查詢O(1)時間複雜度 }; int main(void) { LRUCache lrucache(3); lrucache.set(1,8); lrucache.set(2,9); lrucache.set(3,10); lrucache.set(4,11); cout<<lrucache.get(1)<<endl; cout<<lrucache.get(2)<<endl; cout<<lrucache.get(4)<<endl; return 0; }