✊✊✊🌈大家好!最近有面试会考LRU
题,所以本篇文章将较详细介绍该题目146. LRU 缓存
,代码语言为:C++代码😇。
🎡导航小助手🎡
394. 字符串解码
🔒1、题目:
请你设计并实现一个满足 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)
的平均时间复杂度运行。
🌲 示例 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 <= capacity <= 3000
- 0 <= key <= 10000
- 0 <= value <= 105
- 最多调用 2 * 105 次 get 和 put
来源:力扣(LeetCode)👈
☀️2、思路:
思路参考灵神的题解
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
和value
,节点的两个指针pre
和next
分别指向前一个和后一个节点。还需要哨兵节点dummy
。
定义一个Node类,存放上述变量。 - get()要求关键字key在缓存中,则返回关键字的值,需要用到哈希表。
unordered_map<int, Node*> key_to_node; - put(),缓存中key不存在,需要创建一个节点放在缓存最前列。这样的话,新的节点不断插入到哨兵节点与哨兵节点下一个节点之间,这样的话,哨兵节点的前一个指针,就指向了最久未使用的节点。缓存中节点数超额后,就可以很方便的删除掉最久未使用的节点。
如何删除一个节点?
将上一个节点的后指针跳过当前指针,直接指向后一个指针。(x.pre.next=x.next)
同理。将下一个节点的前指针跳过当前指针,直接指向前一个指针。(x.next.pre=x.pre)
如何把节点插入到哨兵节点dummy和哨兵节点dummy->next后一个指针之间。
cpp
x->pre=dummy;
x->next=dummy->next;
x->pre->next = x;
x->next->pre = x;
复杂度分析 :
⏳时间复杂度 O(1)
:
🏠空间复杂度 O(min(p,capacity))
:其中p为put的调用次数。
🔑3、代码:
cpp
//1.创建节点类
//1.创建节点类
class Node{
public:
int key, value;
Node *pre, *next;
Node(int k = 0, int v = 0) : key(k), value(v) {}
};
class LRUCache {
//2.
private:
//2.1设置相关变量
int capacity;//容量
Node *dummy;//哨兵节点
unordered_map<int,Node*> um;//哈希表
//2.2
//删除
void remove(Node*x){
x->pre->next = x->next;
x->next->pre = x->pre;
}
//添加
void add(Node*x){
x->pre=dummy;
x->next=dummy->next;
x->pre->next = x;
x->next->pre = x;
}
Node* get_node(int key){
auto it=um.find(key);
if(it == um.end())
return nullptr;
auto node = it->second;
remove(node);
add(node);
return node;
}
public:
LRUCache(int capacity):capacity(capacity),dummy(new Node()) {
dummy->pre=dummy;
dummy->next=dummy;
}
int get(int key) {
auto node = get_node(key);
return node ? node->value : -1;
}
void put(int key, int value) {
auto node = get_node(key);
if (node) { // 有这本书
node->value = value; // 更新 value
return;
}
um[key] = node = new Node(key, value); // 新书
add(node); // 放在最上面
if (um.size() > capacity) { // 书太多了
auto back_node = dummy->pre;
um.erase(back_node->key);
remove(back_node); // 去掉最后一本书
delete back_node; // 释放内存
}
}
};