Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get
and put
.
get(key)
- Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.put(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.
Follow up:
Could you do both operations in O(1) time complexity?
Example:
设计一个简单版的最近使用缓存模型。其中,缓存空间有容量限制,时间复杂度要求是o(1).
“最近使用”指的是最近被访问过。
思路:对以上cache的操作有:查找(get),添加(put),替换(put)。因有容量限制,还需要有删除,每次当cache中的容量满了的时候,将最久未使用的节点删除。
为快速添加和删除,我们可以用双向链表来设计cache,链表中从头到尾的数据顺序依次是:
(最近访问)--> ... --> (最旧访问)
1.添加节点:新节点插入到表头即可,时间复杂度为O(1);
2.删除节点:删除链表的末尾节点,时间复杂度为O(1);
3.查找节点:按顺序对链表进行遍历,时间复杂度为O(n);
当节点被查询到时,将节点移动到链表头部。
4.替换节点:查找到后替换节点,将节点移动到链表头部,时间复杂度为O(n).
因此,为了保证在查找和替换节点的时候,由时间复杂度O(n)变为O(1),可以考虑在数据结构中加入hash。
c++代码示例:
#include<list>
#include<unordered_map>
struct CacheNode
{
int key;
int value;
CacheNode(int k, int v){
key = k;
value = v;
}
};
class LRUCache{
public:
LRUCache(int capacity){
}
int get(int key){
if(cacheMap.find(key) == cacheMap.end())
return -1;
else{
//把当前访问的节点移到链表头部,并且更新map中该节点的地址
cacheList.splice(cacheList.begin(), cacheList, cacheMap[key]);
cacheMap[key] = cacheList.begin();
return cacheMap[key]->value;
}
}
void put(int key, int value){
if(cacheMap.find(key) == cacheMap.end())
{
if(cacheList.size() == size){
//删除链表尾部节点(最少访问的节点)
cacheMap.erase(cacheList.back().key);
cacheList.pop_back();
}
//插入新节点到链表头部,并且更新map中增加该节点
cacheList.push_front(CacheNode(key, value));
cacheMap[key] = cacheList.begin();
}
else{
//更新节点到值,把当前访问的节点移到链表头部,并且更新map中该节点的地址
cacheMap[key]->value = value;
cacheList.splice(cacheList.begin(), cacheList, cacheMap[key]);
cacheMap[key] = cacheList.begin();
}
}
private:
list<CacheNode> cacheList;
unordered_map<int, list<CacheNode>::iterator> cacheMap;
int size;
}