场景想象:
你有一个书架(缓存),容量有限(比如只能放 3 本书)。
规则是'最近最少使用 (Least Recently Used)'淘汰:
- 读取:如果你读了一本书,它就变得'新鲜'了,要把它抽出来放到最前面。
- 插入:
- 如果书架没满,新书直接插到最前面。
- 如果书架满了,必须把最后面那本(最久没人看的那本)扔掉,然后把新书插到最前面。
技术难点:
我们需要两个操作:get(查找)和 put(插入/更新)。
题目要求这两个操作的时间复杂度必须都是 $O(1)$。
- 只用数组? 插入/删除慢($O(N)$)。
- 只用链表? 查找慢($O(N)$)。
- 只用对象/Map? 没办法维护'谁是最新的、谁是最旧的'这种顺序。
终极方案:哈希表 + 双向链表
- 哈希表 (Map):负责 $O(1)$ 找到某个节点。
- 双向链表 (Double Linked List):负责 $O(1)$ 移动节点位置(因为双向链表删除节点不需要遍历找前驱)。
力扣 146. LRU 缓存
https://leetcode.cn/problems/lru-cache/

题目分析:
- 输入:
capacity(容量)。 - 实现:
get(key): 如果存在,返回 value,并把它移到最新位置;不存在返回 -1。put(key, value): 如果 key 存在,更新 value 并移到最新;如果不存在,插入新节点(可能需要淘汰最老的)。
核心思维:虚拟头尾 + Map 映射
为了操作方便,我们给双向链表加上 Dummy Head 和 Dummy Tail。
- Head 右边:永远是最新的数据。
- Tail 左边:永远是最旧的数据(淘汰候补)。
代码实现 (JavaScript)
这是一道工程题,代码量稍大,但逻辑非常模块化。
// 1. 定义双向链表节点
class Node {
constructor(key, value) {
this.key = key;
. = value;
. = ;
. = ;
}
}
{
() {
. = capacity;
. = ();
. = (, );
. = (, );
.. = .;
.. = .;
. = ;
}
() {
(!..(key)) {
-;
}
node = ..(key);
.(node);
node.;
}
() {
(..(key)) {
node = ..(key);
node. = value;
.(node);
} {
newNode = (key, value);
..(key, newNode);
.(newNode);
.++;
(. > .) {
removedNode = .();
..(removedNode.);
.--;
}
}
}
() {
node. = .;
node. = ..;
... = node;
.. = node;
}
() {
node.. = node.;
node.. = node.;
}
() {
.(node);
.(node);
}
() {
node = ..;
.(node);
node;
}
}


