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.
题意:实现LRU cache。这里需要了解下LRU替换的本质是替换最长时间没被访问的元素。set和get都算访问操作,故解决方案是cache中的元素是有序的,按照其被访问的时间排序,最新被访问的元素在头部(或尾部),替换时直接删除尾部或者头部元素即可,如果不使用有序序列的话,那么需要记录每个元素最后被访问的时间,替换时,遍历整个序列,找到时间最近的替换掉,但是这样会超时。故应该保持序列的有序。
按照题目要求,要实现get和set功能,为了满足随机存储需求我们首先想到的数组,如果用链表会有o(n)的访问时间,然而又要维护一个least used队列,常用的放在前面,用的少的放在后面。这就要求我们队节点有好的删除和插入操作。这个要求又让我们想到了链表,因为数组的删除和插入操作时0(n)的负责度。
那么我们能不能维护一个数据结构使得访问操作和插入删除操作都是0(1)的复杂度呢?答案是可定的。即使用list保存元素,在头部插入,在尾部删除,每次访问元素时,把它移动到头部(双向链表移动一个元素到头部时间复杂度是0(1));再用一个hash_map记录元素的位置;
难点:如何实现每一次访问元素时,重新调整序列的顺序,让被访问元素移动到头部??
解决:STL中各种容器只有list能方便地调整顺序,就是splice方法,他可以实现某个位置的元素或者某个区间位置的元素插入到当前list中,当然如果二者是同一个list,那么就是调整该list中某些元素的顺序了。,由于其参数为位置迭代器iterator,故我们需要记录元素key对应的迭代器信息,可以使用map,但是由于map是红黑树实现的,故其存取的时间复杂度为o(lgn),由于我们无需有序,故使用hash_map实现即可即unorder_map;
#include<iostream>
#include<list>
using namespace std;
struct node
{
int key;
int value;
node(int k, int v):key(k), value(v){}
};
/*
思路:使用双向list每次set或者get一个元素 都是把这个元素放到list的头部,无需统计每个元素的操作次数;
实际上LRU的意思就是根据元素最后被访问的时间来决定替换哪个,故list中尾部的元素被替换;
STL技巧:1、使用map的find方法来判断key是否已经存在;
2、使用unordered_map,它是hash_map,存取时间是o(1),用他存储元素的position迭代器,是为了方便splice函数的调用;
3、list.splice(position, list, element_pos)的作用是吧list的元素element_pos的元素插入到position位置,本题中是为了移动元素
到list头部;
*/
class LRUCache
{
private:
int size;
list<node> values;
unorder_map<int, list<node>::iterator> positions;
public:
LRUCache(int capacity)
{
size = capacity;
}
int get(int key)
{
if (positions.find(key)!=positions.end())
{
values.splice(values.begin(), values, positions[key]);
positions[key] = values.begin();
return values.begin()->value;
}
return -1;
}
void set(int key, int value)
{
if (positions.find(key) != positions.end())
{
values.splice(values.begin(), values, positions[key]);
values.begin()->value = value;
positions[key] = values.begin();//这里保存的是指针,如果key在链表中进行了更新,这个
//位置也会更新
}
else if(values.size()<size)
{
values.push_front(node(key, value));
positions[key] = values.begin();
}
else
{
node last = values.back();
values.pop_back();
positions.erase(last.key);
values.push_front(node(key, value));
positions[key] = values.begin();
}
}
};