lfu缓存通过freq_map、key_node_map和min_freq三组件协同实现get/put严格o(1):频次链表按lru排序,min_freq在插入时置1、删除后跳过空链表更新,确保最小频次实时准确。

实现 LFU 缓存时,频次更新和最小频次节点定位常退化为 O(n) 时间,导致高并发下缓存吞吐骤降;必须在不牺牲键值映射 O(1) 查找的前提下,让 get/put 操作全程维持严格 O(1) 时间复杂度,包括实时获取当前最小频次。
核心数据结构协同设计
用三个组件构成闭环:freq_map(频次→双向链表)、key_node_map(键→节点指针)、min_freq(整型变量记录当前全局最小频次)。
每个 freq_map[key] 对应一条按访问时间倒序排列的 LRU 风格链表,新访问节点插入表头,保证同频次下最近使用优先保留。
【min_freq 必须在每次 put 或 get 触发频次变更后立即校验更新,否则后续 get 可能误删非最小频次节点】
put 操作的四步原子流程
第一步:若 key 已存在,更新 value 并将对应节点从旧频次链表移至 freq+1 链表头部,同时更新 key_node_map 中的指针指向新位置。
第二步:若 key 不存在且缓存已满,从 freq_map[min_freq] 的尾部删除节点(即最久未用的最小频次节点),并从 key_node_map 中擦除该 key。
第三步:为新 key 创建节点,频次设为 1,插入 freq_map[1] 表头,并写入 key_node_map。
第四步:若删除的是最小频次链表且该链表变为空,则 min_freq 自增 1;若为新增节点,则 min_freq 重置为 1。
get 操作的频次跃迁与链表迁移
方法一:查 key_node_map 得到 node 指针 → 从 freq_map[node->freq] 中摘除该节点 → node->freq++ → 插入 freq_map[node->freq] 表头 → 更新 key_node_map[node->key] = node → 返回 value。
方法二:若 node 是 freq_map[node->freq] 中唯一节点且 node->freq == min_freq,则 min_freq++;该判断必须在摘除节点后、插入前完成,否则 min_freq 可能指向空链表。
这一步操作起来很简单,但顺序不能颠倒:先摘除再判断是否清空,最后才更新频次并插入新链表。
最小频次的 O(1) 维护机制
min_freq 不通过遍历 freq_map 获取,而是在两个时刻被动更新:① 新 key 插入时强制设为 1;② 删除节点后,若 freq_map[min_freq] 为空,则执行 min_freq++ 直至 freq_map[min_freq] 非空或越界。
注意:第二个时刻的 while 循环看似非 O(1),但摊还分析下,每个频次最多被清空一次,总增量不超过总 put 次数,因此均摊仍为 O(1)。
只要不手动修改 min_freq 或跳过空链表检查,该变量始终可信——它不是推导值,而是被严格约束的状态快照。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











