LeetCode146:LRU Cache
题目:
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; }