LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。
思路,主要涉及到查找和更新,采用双向链表加 unordered_map 可以把时间复杂度降为 O(1)
其中,双向链表可以自己实现,也可以采用 STL 中的 list ,其中 list 里面有封装好的实现,可以精简代码
自己实现,更锻炼自己的对链表和 LRU 缓存的理解
基于 list 的实现
先贴上代码 ,其中 splice 函数就是为这一题而生的 : 第一个参数为目标位置,第二参数为要移动的链表,后面的参数用来限定位置,若只有两个参数时,这个函数会把要移动的链表整体移动到目标位置;要是指定位置了,只会移动范围内的,一个参数代表单个节点,两个参数代表,起始点和终点。 这里我们只需要移动一个节点,所以有三个参数。
main 函数可以用第二个实现里面的代码
class LRUCache {
private:
int cap; // 缓存容量
list<pair<int,int>> cacheList; // 双向链表,方便插入和删除
// key 为数据的密钥, value 为存放当前密钥的节点 迭代器
unordered_map<int, list<pair<int, int>>::iterator> cacheMap;
public:
LRUCache( int capacity ) : cap(capacity) {};
int get( int key ) {
auto iter = cacheMap.find(key);
// iter 的类型为 unordered_map<int, list<pair<int, int>>::iterator>::iterator
if (iter==cacheMap.end()) // 查找元素不在容器内
return -1;
// list 的一个快捷方法,参数分别为 位置,所移动的链表,要移动的节点(可以为一个范围)
cacheList.splice( cacheList.begin(), cacheList, iter->second );
return iter->second->second;
}
void put( int key, int value ) {
auto iter = cacheMap.find( key );
if (iter == cacheMap.end()) {
// 新增当前元素
cacheList.push_front({key, value});
cacheMap.insert({ key, cacheList.begin() });
// 若容量超了,则删除末尾节点,并在 哈希表中删除
if (cacheMap.size() > cap)
cacheMap.erase(cacheList.back().first), cacheList.pop_back();
} else {
// 更新当前节点, 并移动到第一个位置