怎么去想:
一般的cache就是一个map,LRU性质需要维护least recently used。当一个key被访问,这个key就处于离被移出cache最远的位置,下一次又有一个key被访问,之前的key就变为离被移除cache次远的位置,这里面就有队列的性质,队列的尾就是least recently used,队头就是最久未被访问的。只不过一般队列只是增加新的,cache key的队列可以把队列里任意位置的一个元素移到队尾。
当访问一个key时,需要立刻定位到LRU队列对应的位置,所以需要一个map 来关联key和 key在LRU中的位置,为了方便删除(不用找前继结点)队列用双向链表。
进一步的,因为双链表节点的移动只是修改指针,节点大小无所谓,所以LRU队列可以不仅放key,而是key和数据都放。这样就不需要单独的map放数据,而是只是一个作为locator的 map 和 一个节点包含key和数据的双链表就够了,locator定位到队列的位置,数据也就定位到了。
class LRUCache{
public:
LRUCache(int capacity) {
this->capacity = capacity;
}
int get(int key) {
auto it = m.find(key);
if(it==m.end()) return -1;
int ret = it->second->second;
l.splice(l.end(), l, it->second);
it->second=prev(l.end());
return ret;
}
void set(int key, int value) {
auto it = m.find(key);
if(it!=m.end()){
it->second->second = value;
l.splice(l.end(), l, it->second);
}else{
if(l.size()==capacity){
m.erase(l.front().first);
l.pop_front();
}
l.push_back(make_pair(key, value));
}
m[key]=prev(l.end());
}
private:
list<pair<int,int>> l;
unordered_map<int,list<pair<int,int>>::iterator> m;
int capacity;
};