题目:
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
问题解析:
设计一个最近使用缓存的结构,使得该缓存具有get和put操作,并且,缓存有容量(capacity)上限。
链接:
思路标签
链表、hash map
解答:
- 为快速添加和删除,我们需要使用链表来设计cache,链表中从头到尾的数据顺序依次是:(最近访问)->…(最旧访问);
- 因为是最近访问的在链表的最前,我们使用list来保存key值,而使用hashmap来保存(key,value)对即可,同时需要对value进行改造,因为我们还需要将每一个value与list中的key结点进行绑定,组成pair,以便每次操作进行list的最近访问的调整;
- 添加节点put(key, value):
- 判断hashmap中是否存在key节点,如果存在则调整List,将key置为头部(最近访问),更新hashmap指向key节点的指针;
- 如果不存在key值对,则判断capacity是否满容,若不满容量则直接在list和hashmap中加入新节点(list在表头添加);否则则删除list末尾key和hashmap中的对应值对,再进行添加。
- 新节点插入到表头即可,时间复杂度O(1)。
- 查找节点get(key):
- 在hashmap中查询,若不存在则返回-1;
- 若存在,则调整list位置更新hashmap对应指针,返回key对应的value;
- 每次节点被查询到时,将节点移动到链表头部,时间复杂度O(1)。
class LRUCache {
public:
LRUCache(int capacity) : _capacity(capacity) {}
int get(int key) {
auto it = cache.find(key);
if(it == cache.end())
return -1;
adjust(it);
return it->second.first;
}
void put(int key, int value) {
auto it = cache.find(key);
if(it != cache.end())
adjust(it);
else{
if(cache.size() == _capacity){
cache.erase(used.back());
used.pop_back();
}
used.push_front(key);
}
cache[key] = {value, used.begin()};
}
private:
typedef pair<int, list<int>::iterator> valListPair;
typedef unordered_map<int, valListPair> keyValMap;
list<int> used;
keyValMap cache;
int _capacity;
void adjust(keyValMap::iterator it){
int key = it->first;
used.erase(it->second.second);
used.push_front(key);
it->second.second = used.begin();
}
};
/**
* 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);
*/