LRU缓存机制
LRU是页面置换算法的其中一个,是一种最近最少使用的缓存机制,它支持以下操作
获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。
我们使用O(1)的时间复杂度完成这两种操作
LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );
cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // 返回 1
cache.put(3, 3); // 该操作会使得密钥 2 作废
cache.get(2); // 返回 -1 (未找到)
cache.put(4, 4); // 该操作会使得密钥 1 作废
cache.get(1); // 返回 -1 (未找到)
cache.get(3); // 返回 3
cache.get(4); // 返回 4
思路:
使用两个数据结构,一个是队列,一个是哈希map
确保通过队列的结点可以找到哈希map,通过哈希map也可以找到队列的结点,我们这样定义
//pair 中的first和second我叫做 key 和value
list< pair<int ,int >> queue;
//m的first是key,这个key和queue中pair的key相对应,m的second的是list的迭代器
unordered_map<int, list<pair<int,int> >::iterator > m;
从上面的结构我们可以发现,通过key经过哈希map的索引我们找到list的迭代器,通过list的迭代器我们就可以找到pair结构的second,也就是value,同样的,在已知list的迭代器的情况下,我们可以通过迭代器中的key去索引到map的迭代器。
根据上面的叙述我们可以捋一下程序的大致流程:
set操作:
我们用一个队列去纪录我们的数据使用的顺序,在队列的最尾端是最早使用的数据,也就是最近使用最少的数据。
1.当set一个key-value后,我们首先寻找这个key是否在哈希map中能找到,如果找到了就找到了对应list中的迭代器了,我们把list对应的那个结点调整到队列首,表示最新使用的,并且把哈希map中key对应的值重新指向调整后的结点
2.如果没有找到说明要增加新数据,先判断容量够不够用,如果够用(size < cap)直接size要++,同时要向队列中插入一个key-value,并且哈希map中也要生成一个key-value指向list中的结点,如果容量(size == cap)不够用就要淘汰最不经常使用的结点(实际上就是list中的尾结点),怎么删除呢? 先用list中尾结点的key值把哈希map中的映射关系删除掉,然后再删除list中的尾结点,最后再添加新结点。
get操作:
get操作就是读取对应key值对应的value值,需要做的就是每次读取后,如果key值存在就把key值对应的list结点调整到首部,表示最新使用的,并且调整哈希map中key对应的结点
如果key没有对应的结点就直接返回-1即可。
代码
class LRUCache {
public:
LRUCache(int capacity):cap(capacity),size(0) {
if(cap == 0)
assert(false);
}
int get(int key) {
int value = -1;
auto it = key_list_map.find(key);
if(it==key_list_map.end())
{
return -1;
}
else
{
value = it->second->second;
ls.erase(it->second);
ls.push_front(pair<int,int>(key,value));
it->second = ls.begin();
return value;
}
}
void put(int key, int value) {
auto it = key_list_map.find(key);
if(it==key_list_map.end())
{
if(cap == size)
{
//用list中的key删除unorderedmap中的关系
key_list_map.erase(ls.back().first);
ls.pop_back();
}
else
{
++size;
}
}
else
{
ls.erase(it->second);//用迭代器删除
}
//重新入一个新的键值对
//重新建立映射关系
ls.push_front(pair<int,int>(key,value));
key_list_map[key] = ls.begin();
}
int cap;
int size;
std::list< std::pair<int,int> > ls;
std::unordered_map<int,std::list< std::pair<int,int> >::iterator> key_list_map;
};
来源:CSDN
作者:4559
链接:https://blog.csdn.net/QQ1910084514/article/details/88376905