146. LRU Cache
题目大意
Design a data structure that follows the constraints of a Least Recently Used (LRU) cache.
Implement the LRUCache
class:
LRUCache(int capacity)
Initialize the LRU cache with positive size capacity.int get(int key)
Return the value of the key if the key exists, otherwise return -1.void put(int key, int value)
Update the value of the key if the key exists. Otherwise, add the key-value pair to the cache. If the number of keys exceeds the capacity from this operation, evict the least recently used key.
The functions get
and put
must each run in O(1) average time complexity.
中文释义
设计一个遵循最近最少使用(LRU)缓存约束的数据结构。
实现 LRUCache
类:
LRUCache(int capacity)
用正整数容量初始化 LRU 缓存。int get(int key)
如果键存在,则返回键的值;否则,返回 -1。void put(int key, int value)
如果键存在,更新其值。否则,将键值对添加到缓存中。如果此操作导致键的数量超出容量,那么淘汰最近最少使用的键。
get
和 put
函数必须都具有 O(1) 的平均时间复杂度。
示例
示例 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
限制条件
- 容量范围为
1 <= capacity <= 3000
。 - 键的范围为
0 <= key <= 104
。 - 值的范围为
0 <= value <= 105
。 - 最多将对
get
和put
调用 2 * 10^5 次。
解题思路
方法
该方案使用双数据结构:一个双向链表(list
)和一个哈希表(unordered_map
),来实现一个 LRU 缓存。双向链表用于维护键值对的顺序,而哈希表用于快速访问链表中的节点。
-
初始化:
- 初始化一个双向链表
cacheList
来存储(key, value)
对。 - 初始化一个哈希表
cacheMap
用于存储键到cacheList
中对应迭代器的映射。
- 初始化一个双向链表
-
移动节点到链表头:
- 实现一个
moveToHead
方法,用于将已存在的节点移动到链表的头部。
- 实现一个
-
获取数据:
get
方法首先检查键是否存在于哈希表中,若不存在返回 -1。- 如果键存在,使用
moveToHead
方法将节点移到链表头,并返回对应的值。
-
存放数据:
put
方法首先检查键是否存在于哈希表中。- 如果键已存在,更新其值并使用
moveToHead
方法移动到链表头部。 - 如果键不存在,插入新的键值对到链表头,并更新哈希表。
- 如果插入新元素后链表大小超过容量,则删除链表尾部的元素并从哈希表中移除对应的键。
代码
class LRUCache {
private:
int capacity;
list<pair<int, int>> cacheList; // 存储 (key, value) 对
unordered_map<int, list<pair<int, int>>::iterator> cacheMap; // Key -> cacheList 中对应迭代器的映射
// 将节点移动到链表头部
void moveToHead(int key, int value) {
cacheList.erase(cacheMap[key]);
cacheList.push_front(make_pair(key, value));
cacheMap[key] = cacheList.begin();
}
public:
LRUCache(int capacity) : capacity(capacity) {}
int get(int key) {
if (cacheMap.find(key) == cacheMap.end()) {
return -1; // key 不存在
}
// 将节点移动到链表头部,并返回对应的 value
moveToHead(key, cacheMap[key]->second);
return cacheMap[key]->second;
}
void put(int key, int value) {
if (cacheMap.find(key) != cacheMap.end()) {
// 更新已有 key 的 value 并移动到链表头部
moveToHead(key, value);
} else {
// 插入新 key-value 对
cacheList.push_front(make_pair(key, value));
cacheMap[key] = cacheList.begin();
if (cacheList.size() > capacity) {
// 如果超出容量,删除链表尾部元素
cacheMap.erase(cacheList.back().first);
cacheList.pop_back();
}
}
}
};