【算法】LRU缓存
1.概念介绍
假设缓存的大小固定,初始状态为空。每发生一次读内存操作,首先查找待读取的数据是否
存在于缓存中,如果存在则缓存命中,返回数据,并将缓存数据放到缓存区头部位置;否则缓存未命中,返回提示信息。
向缓存添加数据时,如果缓存已满,则需要删除访问时间最早的数据,这种更新缓存的方法就叫做LRU(Least Recently Used)。
2. 实际实现LRUCache类
基本要求如下
- LRUCache(int capacity) 用一个正整数表示的容量大小初始化缓存空间。
- int get(int key) 如果对应的键存在于缓存中,即命中返回键对应的值,未命中返回-1。
- void put(int key, int value) 如果已存在更新其内容。否则将键值对添加到缓存中。 如果超过了最大容量,淡出最近最少使用的键值对。
同时要求get和put操作保证平均时间复杂度为O(1)。
案例介绍
-
示例输入
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]] -
输出
[null, null, null, 1, null, -1, null, -1, 3, 4] -
过程解释
LRUCache lRUCache = new LRUCache(2);
LRUCache.put(1, 1); // 缓存内容变为 {1=1}
LRUCache.put(2, 2); // 缓存内容变为 {1=1, 2=2}
LRUCache.get(1); // 返回键为1的缓存内容对应的值
LRUCache.put(3, 3); // LRU键为2, 添加到缓存中前先移除键为2的缓存内容然后添加新内容, 缓存内容变为 {1=1, 3=3}
LRUCache.get(2); // 缓存中不存在就返回-1
LRUCache.put(4, 4); // LRU键为1, 先移除键为1的缓存内容然后添加新内容, 缓存内容变为 {4=4, 3=3}
LRUCache.get(1); // 缓存中不存在就返回-1
LRUCache.get(3); // 返回3
LRUCache.get(4); // 返回4
3.C++参考实现
class LRUCache {
public:
struct ListNode {
int key;
int val;
ListNode *prev;
ListNode *next;
ListNode(): key(0), val(0), prev(nullptr), next(nullptr) {}
ListNode(int _key, int _val): key(_key), val(_val),
prev(nullptr), next(nullptr) {}
};
public:
LRUCache(const int capacity): cap(capacity) {
head = new ListNode(-1, 0);
tail = new ListNode(-1, 0);
head->next = tail;
tail->prev = head;
}
~LRUCache(void) {
if (cap>0)
{
ListNode *curNode = head->next;
// 释放链表中除首尾指示结点外所有结点占用的资源
while (curNode != tail)
{
ListNode *tmpNode = curNode;
curNode = curNode->next;
deletenode(tmpNode);
}
// 清空哈希表
mp.clear();
// 重置缓存容量
cap = 0;
}
// 释放首尾指针资源并将其设置为空指针
delete head;
delete tail;
head = nullptr;
tail = nullptr;
}
int get(int key) {
if (mp.find(key)==mp.end())
return -1;
ListNode *curNode = mp[key];
// 如果已存在于当前缓存中,将其提前到链表首部位置
moveToHead(curNode);
return curNode->val;
}
void put(int key, int val) {
if (mp.find(key)!=mp.end())
{
ListNode *curNode = mp[key];
// 如果已存在于当前缓存中,将其提前到链表首部位置
moveToHead(curNode);
curNode->val = val;
} else {
if (cap>0 && mp.size()==cap)
{
// 删除最后一个结点,因为时间最久没被访问
deleteNode(tail->prev);
}
// 在链表头部添加新结点
ListNode *tmpNode = new ListNode(key, val);
mp[key] = tmpNode;
addNode(tmpNode);
}
}
private:
void moveToHead(ListNode *pnode)
{
// 断开当前结点与前后结点的连接
curNode->prev->next = curNode->next;
curNode->next->prev = curNode->prev;
// 将结点连到链表头部位置
curNode->next = head->next;
head->next->prev = curNode;
head->next = curNode;
curNode->prev = head;
}
void deleteNode(ListNode *pnode)
{
pnode->prev->next = pnode->next;
pnode->next->prev = pnode->prev;
delete pnode;
pnode = nullptr;
}
void addNode(ListNode *pnode)
{
pnode->next = head->next;
head->next->prev = pnode;
head->next = pnode;
pnode->prev = head;
}
private:
int cap;
std::unordered_map<int, ListNode*> mp;
ListNode *head;
ListNode *tail;
};
本文作者 :phillee
发表日期 :2022年03月07日
本文链接 :https://www.cnblogs.com/phillee/p/15975167.html
版权声明 :自由转载-非商用-非衍生-保持署名(创意共享3.0许可协议/CC BY-NC-SA 3.0)。转载请注明出处!
限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。