LRUCache的原理
LRU是Least Recently Used的缩写,意思是最近最少使用,它是一种Cache替换算法。 什么是
Cache?狭义的Cache指的是位于CPU和主存间的快速RAM, 通常它不像系统主存那样使用
DRAM技术,而使用昂贵但较快速的SRAM技术。 广义上的Cache指的是位于速度相差较大的两种
硬件之间, 用于协调两者数据传输速度差异的结构。除了CPU与主存之间有Cache, 内存与硬盘
之间也有Cache,乃至在硬盘与网络之间也有某种意义上的Cache── 称为Internet临时文件夹或
网络内容缓存等。
Cache的容量有限,因此当Cache的容量用完后,而又有新的内容需要添加进来时, 就需要挑选
并舍弃原有的部分内容,从而腾出空间来放新内容。LRU Cache 的替换原则就是将最近最少使用
的内容替换掉。其实,LRU译成最久未使用会更形象, 因为该算法每次替换掉的就是一段时间内
最久没有使用过的内容。
LRUCache的实现
首先需要考虑的是数据结构,需要用一个结构来保存数据,另一个来记录使用的顺序。对于记录顺序的结构,因为可能存在大量的插入以及移动,为了能够保证其插入删除的效率一直在O(1),所以需要用到链表。而为了能够实现O(1)的查找,存储结构需要用到哈希,并且为了能够保证更新顺序时能够O(1)找到链表中对应的结点,还需要保存其迭代器作为哈希的value。
//利用哈希表来存储数据以及迭代器,来实现o(1)的查找
unordered_map<int, list<pair<int, int>>::iterator> _hashmap;
//利用双向链表来保存缓存使用情况,并保证o(1)的插入删除
list<pair<int, int>> _lrulist;
size_t _capacity;
具体的思路我都写在了注释中
#include<unordered_map>
#include<list>
using namespace std;
class LRUCache
{
public:
LRUCache(size_t capacity)
: _capacity(capacity)
{}
int get(int key)
{
auto ret = _hashmap.find(key);
//查找成功
if (ret != _hashmap.end())
{
//获取数据的位置
list<pair<int, int>>::iterator pos = ret->second;
pair<int, int> kv = *pos;
//将数据移动到队首
_lrulist.erase(pos);
_lrulist.push_front(kv);
ret->second = _lrulist.begin();
return kv.second;
}
//查找失败返回-1
else
{
return -1;
}
}
void put(int key, int value)
{
//首先判断数据是否存在,来判定是插入还是更新
auto ret = _hashmap.find(key);
//如果数据存在,则是更新
if (ret != _hashmap.end())
{
list<pair<int, int>>::iterator pos = ret->second;
_lrulist.erase(pos);
_lrulist.push_front(make_pair(key, value));
ret->second = _lrulist.begin();
}
//不存在则是删除
else
{
//数据已满
if (_lrulist.size() == _capacity)
{
//删除最久未使用的数据
pair<int, int> back = _lrulist.back();
_lrulist.pop_back();
_hashmap.erase(back.first);
}
//插入数据
_lrulist.push_front(make_pair(key, value));
_hashmap.insert(make_pair (key, _lrulist.begin()));
}
}
private:
//利用哈希表来存储数据以及迭代器,来实现o(1)的get和put
unordered_map<int, list<pair<int, int>>::iterator> _hashmap;
//利用双向链表来保存缓存使用情况,并保证o(1)的插入删除
list<pair<int, int>> _lrulist;
size_t _capacity;
};