LeetCode146. LRU 缓存(2024秋季每日一题 37)
请你设计并实现一个满足 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) 的平均时间复杂度运行。
示例:
输入
[“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); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2); // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1); // 返回 -1 (未找到)
lRUCache.get(3); // 返回 3
lRUCache.get(4); // 返回 4
提示:
1
<
=
c
a
p
a
c
i
t
y
<
=
3000
1 <= capacity <= 3000
1<=capacity<=3000
0
<
=
k
e
y
<
=
10000
0 <= key <= 10000
0<=key<=10000
0
<
=
v
a
l
u
e
<
=
1
0
5
0 <= value <= 10^5
0<=value<=105
最多调用
2
∗
1
0
5
2 * 10^5
2∗105 次 get
和 put
思路:双链表 + 哈希表
时间复杂度:get:
O
(
1
)
O(1)
O(1)、put:
O
(
1
)
O(1)
O(1)
- 初始化缓存时,创建 head,tail 节点,并且相互指向彼此,初始化容器容量、容器节点数量
- get 时:
- 如果当前 key 存在,则将当前 key 对应的节点移到链表头部(通过 map 保存),并返回 节点的 val 值
- 如果当前 key 不存在,则直接返回
-1
- put 时:
- 如果当前 key 存在,则通过 hash 表 找到对应的节点,将此节点挪到链表头部,并更新节点的 val 值
- 如果当前 key 不存在
- 如果链表中 当前节点数量 < 容量,则新建节点,将节点添加到链表 头部
- 如果链表中 当前节点数量 >= 容量,则删除尾部节点,新建节点,将节点添加到 链表头部
- 更新链表中节点 cnt 数量
map:unordered_map<int, Node *> mp;
struct Node{
Node * pre, * ne;
int key, val;
Node(){
pre = nullptr;
ne = nullptr;
key = 0;
val = 0;
}
Node(int _key, int _val){
key = _key, val = _val;
pre = ne = nullptr;
}
};
class LRUCache {
private:
Node * head;
Node * tail;
unordered_map<int, Node *> mp;
int cnt;
int n;
public:
LRUCache(int capacity) {
cnt = 0;
n = capacity;
head = new Node();
tail = new Node();
head -> ne = tail;
tail -> pre = head;
}
int get(int key) {
if(!mp.count(key)){
return -1;
}
Node * cur = mp[key];
moveToHead(cur);
return cur -> val;
}
void put(int key, int value) {
if(!mp.count(key)){
cnt++;
Node * cur = new Node(key, value);
if(cnt > n){
removeTail();
addToHead(cur);
cnt = n;
}else{
addToHead(cur);
}
//mp[key]= cur;
mp.insert({key, cur});
}else{
Node * cur = mp[key];
cur -> val = value;
moveToHead(cur);
}
}
void removeTail(){
mp.erase(tail -> pre -> key);
tail -> pre = tail ->pre -> pre;
tail -> pre -> ne = tail;
}
void addToHead(Node * cur){
cur -> pre = head;
cur -> ne = head -> ne;
head -> ne -> pre = cur;
head -> ne = cur;
}
void moveToHead(Node * cur){
cur -> ne -> pre = cur -> pre;
cur -> pre -> ne = cur -> ne;
addToHead(cur);
}
};