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.
分析:
get(int key),首先判断key值是否在缓存队列中,如果在则返回对应的value,同时更新缓存队列中的顺序,如果key值在缓存中,则将key所和value对置于缓存队列的第一个。如果不在则返回-1。
set(key,value),首先判断key值是否在缓存中,如果在缓存中,则将key的value值更改,并且更改缓存队列中的顺序;如果不在缓存队列中,判断队列的大小是否达到容量,如果小于容量则在缓存队列的头部插入key和value对即可,如果达到容量,则需要先删除缓存队列最后一个元素,再在队列头部插入key和value。
尝试1:使用map保存key和value对,用list表示页面访问的顺序,list从begin到end表示使用时间离现在最近到最远的。
超时的代码:
struct LRUNode{
int key;
int value;
LRUNode(int k,int v):key(k),value(v){}
};
class LRUCache{
public:
LRUCache(int capacity) {
size = capacity;
}
int get(int key) {
map<int,int>::iterator iter = dataMap.find(key);
if(iter != dataMap.end()){
list<LRUNode*>::iterator it = lruList.begin();
for(it; (*it)->key != key; ++it);
LRUNode* node = *it;
lruList.erase(it);
lruList.push_front(node);
return iter->second;
}else{
return -1;
}
}
void set(int key, int value) {
if(get(key) == -1){
LRUNode *newNode = new LRUNode(key,value);
if(dataMap.size() >= size){
int lastKey = (*lruList.rbegin())->key;
dataMap.erase(lastKey);
lruList.pop_back();
}
dataMap.insert(make_pair(key,value));
lruList.push_front(newNode);
}else{
(*lruList.begin())->value = value;
dataMap[key] = value;
}
}
private:
list<LRUNode*> lruList;
map<int,int> dataMap;
int size;
};
思考:上面list查找是从头开始遍历,效率低,造成超时,后来也没想到什么好的提升效率的方法。搜索看到http://www.cnblogs.com/x1957/p/3485053.html
用unordered_map来保存,减少了map默认用红黑树造成排序的时间;保存的为key和在list中的iterator,手法很巧妙,提高了查找的时间,并且在list查找节点中用到了list中的少见的函数:
void splice (iterator position, list& x, iterator i);
此函数是将list x中迭代器位置为i的元素移动到position位置,原迭代器位置的元素删除。
参考的代码:
struct CacheNode{
int key;
int value;
CacheNode(int k , int v) : key(k) , value(v){}
};
class LRUCache{
public:
LRUCache(int capacity) {
size = capacity;
}
int get(int key) {
if(cacheMap.find(key) != cacheMap.end()){
auto it = cacheMap[key];
cacheList.splice(cacheList.begin() , cacheList , it);
cacheMap[key] = cacheList.begin();
return cacheList.begin()->value;
}else{
return -1;
}
}
void set(int key, int value) {
if (cacheMap.find(key) == cacheMap.end()){
if(cacheList.size() == size){
cacheMap.erase(cacheList.back().key);
cacheList.pop_back();
}
cacheList.push_front(CacheNode(key , value));
cacheMap[key] = cacheList.begin();
}else{
auto it = cacheMap[key];
cacheList.splice(cacheList.begin() , cacheList , it);
cacheMap[key] = cacheList.begin();
cacheList.begin()->value = value;
}
}
private:
int size;
list<CacheNode> cacheList;
unordered_map<int , list<CacheNode>::iterator > cacheMap;
};
尝试2、将代码中的map改进为unordered_map后不超时,Accepted。
由此,当存储键值对,而不需要对键值进行排序时,尽量使用unordered_map,这样可以极大提高效率。
struct LRUNode{
int key;
int value;
LRUNode(int k,int v):key(k),value(v){}
};
class LRUCache{
public:
LRUCache(int capacity) {
size = capacity;
}
int get(int key) {
unordered_map<int,int>::iterator iter = dataMap.find(key);
if(iter != dataMap.end()){
list<LRUNode*>::iterator it = lruList.begin();
for(it; (*it)->key != key; ++it);
LRUNode* node = *it;
lruList.erase(it);
lruList.push_front(node);
return iter->second;
}else{
return -1;
}
}
void set(int key, int value) {
if(get(key) == -1){
LRUNode *newNode = new LRUNode(key,value);
if(dataMap.size() >= size){
int lastKey = (*lruList.rbegin())->key;
dataMap.erase(lastKey);
lruList.pop_back();
}
dataMap.insert(make_pair(key,value));
lruList.push_front(newNode);
}else{
(*lruList.begin())->value = value;
dataMap[key] = value;
}
}
private:
list<LRUNode*> lruList;
unordered_map<int,int> dataMap;
int size;
};