LRU Cache
LeetCode #146
Description:
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:
LRUCache cache = new LRUCache( 2 /* capacity */ );
cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // returns 1
cache.put(3, 3); // evicts key 2
cache.get(2); // returns -1 (not found)
cache.put(4, 4); // evicts key 1
cache.get(1); // returns -1 (not found)
cache.get(3); // returns 3
cache.get(4); // returns 4
Idea:
List存CachNode, unordered_map存(key,list里的position(iterator)
为了使查找、插入和删除都有较高的性能,我们使用一个双向链表 (std::list) 和一个哈希表
(std::unordered_map),因为:
• 哈希表保存每个节点的地址,可以基本保证在 O(1) 时间内查找节点
• 双向链表插入和删除效率高,单向链表插入和删除时,还要查找节点的前驱节点
具体实现细节:
• 越靠近链表头部,表示节点上次访问距离现在时间最短,尾部的节点表示最近访问最少
• 访问节点时,如果节点存在,把该节点交换到链表头部,同时更新 hash 表中该节点的地址
• 插入节点时,如果 cache 的 size 达到了上限 capacity,则删除尾部节点,同时要在 hash 表中删
除对应的项;新节点插入链表头部
entire list (1)
void splice (iterator position, list& x);
single element (2)
void splice (iterator position, list& x, iterator i);
element range (3)
void splice (iterator position, list& x, iterator first, iterator last);
The first version (1) transfers all the elements of x into the container.
The second version (2) transfers only the element pointed by i from x into the container.
The third version (3) transfers the range [first,last) from x into the container.
Code:
Method 1
不用list splice, 效率不是很高,但是自己手写的容易懂。
更新的时候,直接删除,然后在list beginning插入。
class LRUCache {
public:
LRUCache(int capacity) {
capacity_ = capacity;
}
int get(int key) {
if(cache_map_.find(key) == cache_map_.end()) return -1;
int value=cache_map_[key]->value;
cache_list_.erase(cache_map_[key]);
cache_list_.insert(cache_list_.begin(), CacheNode(key, value));
cache_map_[key]=cache_list_.begin();
return value;
}
void put(int key, int value) {
// Not exist, just add the new element
if(cache_map_.find(key) != cache_map_.end()){
cache_list_.erase(cache_map_[key]);
cache_list_.insert(cache_list_.begin(), CacheNode(key, value));
cache_map_[key]=cache_list_.begin();
}
// Already exist
else{
// Over capacity, need to delete the least frequenct element
if(cache_list_.size() == capacity_){
cache_map_.erase(cache_list_.back().key);
cache_list_.pop_back();
}
// deal with both under capacity, and over capacity but after deletion
cache_list_.push_front(CacheNode(key, value));
cache_map_[key] = cache_list_.begin();
}
}
private:
struct CacheNode{
int key;
int value;
CacheNode(int k, int v):key(k), value(v){}
};
int capacity_;
list<CacheNode> cache_list_;
unordered_map<int, list<CacheNode>::iterator> cache_map_;
};
Method 2
用list splice
class LRUCache {
public:
LRUCache(int capacity) {
capacity_ = capacity;
}
int get(int key) {
if(cache_map_.find(key) == cache_map_.end()) return -1;
auto it = cache_map_[key];
cache_list_.splice(cache_list_.begin(), cache_list_, it);
// cache_map_[key] = cache_list_.begin(); // list splice does not change iterator
return cache_map_[key]->value;
}
void put(int key, int value) {
// Already exist, just add the new element
if(cache_map_.find(key) != cache_map_.end()){
cache_map_[key]->value = value;
cache_list_.splice(cache_list_.begin(), cache_list_, cache_map_[key]);
// cache_map_[key] = cache_list_.begin(); // list splice does not change iterator
}
// Not exist
else{
// Over capacity, need to delete the least frequenct element
if(cache_list_.size() == capacity_){
cache_map_.erase(cache_list_.back().key);
cache_list_.pop_back();
}
// deal with both under capacity, and over capacity but after deletion
cache_list_.push_front(CacheNode(key, value));
cache_map_[key] = cache_list_.begin();
}
}
private:
struct CacheNode{
int key;
int value;
CacheNode(int k, int v):key(k), value(v){}
};
int capacity_;
list<CacheNode> cache_list_;
unordered_map<int, list<CacheNode>::iterator> cache_map_;
};
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache obj = new LRUCache(capacity);
* int param_1 = obj.get(key);
* obj.put(key,value);
*/