第4章|链表
Unit 13 · 哈希表与双向链表综合设计
定位交给哈希表,
顺序交给双向链表。
LRU 缓存要求查询、更新、移动和淘汰都在平均 O(1) 时间完成。单一结构无法同时满足这些要求,因此本单元将整章链表能力编译成一个真实的数据结构设计。
题目要求
设计一个遵循最近最少使用策略的缓存。get(key) 在键存在时返回值并将该键标记为最近使用;put(key, value) 更新或插入键值,并在超过容量时淘汰最久未使用的键。
复杂度目标
get:平均 O(1)put:平均 O(1)- 空间:O(capacity)
操作示例
顺序约定
本页所有链表从左到右表示:最近使用 MRU → 最久未使用 LRU。业务节点位于两个哨兵 head 与 tail 之间。
从 Unit 12 到 Unit 13:映射继续负责“身份定位”,链表开始负责“顺序修改”
哈希映射保存“原节点 → 副本节点”,解决复杂引用中的对象定位。
哈希映射保存“key → 链表节点”,双向链表负责 O(1) 删除、移动和淘汰。
LRU 是最久未使用节点;MRU 是最近使用节点。每次成功访问都会改变顺序。
head 与 tail 不保存业务数据,用来统一空链表、首节点和尾节点的边界操作。
删除任意节点时,同时更新前驱的 next 与后继的 prev,才能保持双向结构一致。
哈希表提供 O(1) 定位;双向链表提供 O(1) 顺序修改。两者必须始终同步。
根据 key 直接得到节点对象。
已知节点后直接修改相邻引用。
capacity=2,依次 put(1)、put(2)、get(1) 后,谁是 LRU?
#460 LFU 缓存
LRU 只维护时间顺序;LFU 需要先按访问频率选择淘汰桶,再在同频节点中按 LRU 淘汰。迁移重点不是背一份长代码,而是理解三份状态怎样协同保持平均 O(1)。
题目要求
实现 LFUCache(capacity)、get(key) 和 put(key,value)。键不存在时 get 返回 -1;容量满时先淘汰访问频率最低的键,若频率相同,则淘汰其中最久未使用的键。
关键规则与约束
- 新插入键的使用计数从
1开始; - 成功的
get,以及对已有键的put,都会使频率加一; - 同频率时使用 LRU 规则打破平局;
get与put均要求平均O(1);1 ≤ capacity ≤ 10⁴,调用次数最多2×10⁵。
LFUCache(2), put(1,1), put(2,2), get(1), put(3,3), get(2), get(3), put(4,4), get(1), get(3), get(4)输出:[null,null,null,1,null,-1,3,null,-1,3,4]读题门槛
从一条 LRU 链,扩展为“频率桶 + 桶内 LRU”
| #146 LRU | #460 LFU | 迁移含义 |
|---|---|---|
cache[key] → node | nodes[key] → node | O(1) 定位节点保持不变 |
| 一条 MRU→LRU 双向链 | freq_lists[f] 各维护一条 LRU 链 | 先按频率分桶,再在桶内维护时间 |
tail.prev 是淘汰对象 | freq_lists[min_freq] 的 LRU 端是淘汰对象 | min_freq 直接定位最低频桶 |
| 访问后移动到链头 | 从旧频桶删除,频率加一,插入新频桶 MRU 端 | 一次 touch 跨越两条链 |
排列一次成功访问 _touch(node) 的依赖顺序
点击下列步骤组成执行序列。错误顺序会清空重试。
四个语义槽位:先确定职责,再组装代码
touch(node): 从 freq_lists[node.freq] 删除 若清空了 min_freq 桶:min_freq += 1 node.freq += 1 加入 freq_lists[node.freq] 的 MRU 端 evict(): 从 freq_lists[min_freq] 的 LRU 端删除 同步删除 nodes[victim.key]
轨迹中必须观察的两层优先级
min_freq 对应的桶选择淘汰对象。min_freq = 1。独立重写 LFU 核心
辅助类 Node 与 DoublyLinkedList 已提供 add_front/remove/pop_lru/is_empty。请独立实现 LFUCache 的状态、_touch、get 与 put。
这是链表章节终局。可以继续复看 LRU 与 LFU 的结构差异,不会自动跳转。
Unit 13 Skill 卡:用复合结构同时获得 O(1) 定位与 O(1) 顺序修改
你已经完成 LRU 主结构,并通过官方 LFU 样例与独立重写验证了“频率优先、同频按最近使用”的两级淘汰逻辑。页面会停留在这里。