LRU(LeastRecentlyUsed) 最近最少使用
原理
LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”
详情看漫画图解LRU
实现
为了使得查找 插入 和删除都有较高的性能.
-
通过一个双向链表(std::list)保存数据,和一个哈希表(std::unordered_map).
-
哈希表保存每个节点的地址,可以基本保证在O(1)的时间负责度内找到节点.
-
双向链表的插入和删除效率高.
将新插入的节点放到链表的头部,尾部的链表是比较少访问的.
具体实现:
插入节点:
-
若
键
存在,则更新其值
然后将该节点放到链表的头部.并且更新哈希表.
-
若
键
不存在,则创建一个新的节点插入插入时,若缓存区已经满了.
则需将链表的尾节点从链表和哈希表删除.
若缓存未满,直接插入.
最后将 新节点和其索引更新进哈希表
取节点:
-
若
键
不存在,直接返回NULL -
若
键
存在从哈希表中获得其索引,然后在列表中取到该 键值对.
将这个节点更新到链表头部.
同时哈希表该节点在列表中的索引.
LeetCode 146. LRU Cache
#include "MyInclude.h"
/*
Design and implement a data structure for Least Recently Used (LRU) cache.
It should support the following operations: get and put.
get(key) - Get the value (will always be positive) of the key
if the key exists in the cache, otherwise return -1.
put(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.
Follow up:
Could you do both operations in O(1) time complexity?
在常数的时间复杂度要求下完成
*/
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache obj = new LRUCache(capacity);
* int param_1 = obj.get(key);
* obj.put(key,value);
*/
// Least Recently Used (LRU) https://blog.csdn.net/wydyd110/article/details/84023688
// list::splice https://blog.csdn.net/Wchenchen0/article/details/83058928
namespace pro146_0 {
class LRUCache {
public:
class CacheNode {
public:
int key;
int value;
CacheNode(int key, int value) :key(key), value(value) {
}
};
int capacity;
list<CacheNode*> m_list;
unordered_map<int, list<CacheNode*>::iterator> m_map;
/*
m_map存放的是key,迭代器
m_list存放的是 key,value节点,并且,最新的节点放到最前面,而最少用的节点放在了后面
*/
LRUCache(int capacity) {
this->capacity = capacity;
}
int get(int key) {
// 找不到
if (m_map.find(key) == m_map.end()) {
return -1;
} else {
m_list.splice(m_list.begin(), m_list, m_map[key]);
m_map[key] = m_list.begin();
return m_list.front()->value;
}
}
void put(int key, int value) {
/*
插入节点:
1. 节点已经存在,跟新节点.并将节点放到最前面.
2. 节点不存在,在链表最前面插入一个节点
需要检查是否已经满了
*/
if (m_map.find(key) == m_map.end()) {
if (m_list.size() == capacity) {
// 把链表的最后一个元素从链表和哈希表中删除
m_map.erase(m_list.back()->key);
m_list.pop_back();
}
// 插入新节点
m_list.push_front(new CacheNode(key, value));
m_map[key] = m_list.begin();
} else {
// 已经存在更新域值
m_list.splice(m_list.begin(), m_list, m_map[key]);
m_map[key] = m_list.begin();
m_list.front()->value = value;
}
}
};
}