LeetCode_0146. LRU缓存
题目描述
请你设计并实现一个满足 LRU (最近最少使用) 缓存约束的数据结构。
实现 LRUCache 类:
- LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
- int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
- void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。
- 函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。
分析
- get() 和 put() 操作需O(1)的时间按key查找 --> unordered_map
- 逐出LRU关键字需O(1)的时间 --> 需O(1)的时间维护容器内关键字的使用情况:hash定位后能O(1)的时间在增删移 --> 双向链表
设计
- 维护一个双向链表list<pair<int, int>>,按上一次使用时间排列关键字和值,每次操作激活的键值对被移动到链表尾。
- 维护一个哈希表unordered_map<int, list<pair<int, int>>::iterator>,映射关键字和链表中迭代器,每次对链表结点的插入/删除操作都要注意更新结点迭代器。因为list的迭代器不因其它元素的增删改而失效,可以这样持久保存。
实现
class LRUCache {
private:
int capacity;
unordered_map<int, list<pair<int, int>>::iterator> listIterMap;
list<pair<int, int>> vkList;
public:
LRUCache(int _capacity) : capacity(_capacity) {}
int get(int key) {
auto lIt = listIterMap.find(key);
if(lIt == listIterMap.end()) {
return -1;
}
// 激活关键字:移动操作 = 删除 + 表尾插入
pair<int, int> tmp = *(lIt->second);
vkList.erase(lIt->second);
lIt->second = vkList.insert(vkList.end(), tmp);
return tmp.first;
}
void put(int key, int value) {
auto lIt = listIterMap.find(key);
if(lIt != listIterMap.end()) {
(*(lIt->second)).first = value;
pair<int, int> tmp = *(lIt->second);
vkList.erase(lIt->second);
lIt->second = vkList.insert(vkList.end(), tmp);
return;
}
// 队满时删除表头
if(vkList.size() == capacity) {
int delKey = vkList.begin()->second;
vkList.pop_front();
listIterMap.erase(delKey);
}
// 在表尾插入新关键字
auto nIt = vkList.insert(vkList.end(), make_pair(value, key));
listIterMap.emplace(make_pair(key, nIt));
return;
}
};