前两天有个面试问我了一个问题?拿什么容器实现LRU最合适
当时脑子很乱,第一次说用堆来存储,后面又想到改为链表存储更适合其位置不断变化的特征就匆匆过去了
后来自己想了想又查了点资料,最终以下文的方式实现LRU
为了保持高效的put以及find我们在这里需要用到底层为哈希的容器提高查询效率,加上改变位置的灵活性,这里我选择使用了unordered_map以及list的组合方式
class LRUCache {
public:
LRUCache(int capacity) {
_capacity = capacity;
}
int get(int key) {
// 如果key对应的值存在,则listit取出
//这里就可以看出hashmap的value存的是list的iterator 的好处:找到key
// 就找到key存的值在list中的iterator
//直接删除,再进行头插,实现O(1)的数据挪动
auto hashit = _hashmap.find(key);
if(hashit != _hashmap.end()){
auto listit = hashit->second;
pair<int, int> kv = *listit;
_list.erase(listit);
_list.push_front(kv);
_hashmap[key] = _list.begin();
return kv.second;
}
else{
return -1;
}
}
void put(int key, int value) {
// 如果没有数据则进行插入数据
// 如果有数据则进行数据更新
auto hashit = _hashmap.find(key);
if(hashit == _hashmap.end()){
// 插入数据时,如果数据已经达到上限,则删除链表头的数据和hashmap中的数据
//两个删除操 作都是O(1)
if(_list.size() >= _capacity){
_hashmap.erase(_list.back().first);
_list.pop_back();
}
_list.push_front(make_pair(key,value)); _
hashmap[key] = _list.begin();
}
else{
// 再次put,将数据挪动list前面
auto listit = hashit->second;
pair<int, int> kv = *listit;
kv.second = value;
_list.erase(listit);
_list.push_front(kv);
_hashmap[key] = _list.begin();
}
}
private:
list<pair<int, int>> _list;
// 将近用过的往链表的投上移动,保持LRU
size_t _capacity;
// 容量大小,超过容量则换出,保持LRU
unordered_map<int, list<pair<int, int>>::iterator> _hashmap;
//巧妙的设计将unordered_map的value type放成list<pair<int, int>>::iterator
//get一个已有的值可以直接找到key在list中对应的iterator
//然后将这个值移动到链表的头部,保持LRU
};
//测试用例
int main()
{
LRUCache myCache = new LRUCache(2);
myCache.put(1, 1);
myCache.put(2, 2);
cache.get(1);//返回1
cache.put(3, 3);//密钥2作废
myCache.get(2);
myCache.put(4, 4);//密钥1作废
myCache.get(1);//返回-1,未找到
myCache.get(3);//返回3
myCache.get(4);//返回4
return 0;
}