min_freq 必须惰性校验且仅在插入新key或旧桶清空时递增;淘汰前需用find()而非[]检查桶存在性并跳过空桶,node必须显式存freq字段以确保移动中频次可信。

LFU 缓存中 min_freq 无法 O(1) 定位,本质不是算法没想清楚,而是结构更新和变量维护脱节——min_freq 指向一个可能已空的频次桶,而你还在对它调用 .back(),结果未定义行为或崩溃。
为什么 freq_to_list[min_freq].back() 会 crash
常见错误是直接用 operator[] 访问 std::unordered_map<int std::list>></int>:
-
freq_to_list[min_freq]若min_freq不存在,会隐式构造一个空std::list,再调用.back()触发std::out_of_range - 即使存在,若该桶刚被清空(比如所有
freq=2的节点全被get升到freq=3),.back()仍会崩溃 - 多线程下更危险:一个线程在
evict()中检查.empty(),另一线程同时删掉最后一个节点,检查就失效了
min_freq 只能在两个时机安全更新
它不是“当前最小频次”,而是“下一个可淘汰频次”的游标。更新只发生在:
-
put(key, value)插入新 key 时:无条件置min_freq = 1,因为新节点必入freq_to_list[1] -
get(key)或put命中导致某节点从old_freq移出后,且freq_to_list[old_freq].empty()为真、old_freq == min_freq:此时必须min_freq++
注意:min_freq 永远不会下降;get 不触发降级;evict() 开始前不主动扫描,只做惰性校验。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
淘汰前必须用 find() 而非 operator[]
判断桶是否有效,必须绕过隐式构造陷阱:
auto it = freq_to_list.find(min_freq);
while (it == freq_to_list.end() || it->second.empty()) {
min_freq++;
it = freq_to_list.find(min_freq);
// 加防护:if (min_freq > MAX_FREQ) { min_freq = 1; break; }
}
Node& victim = it->second.back();
- 永远不用
freq_to_list[min_freq]做存在性判断 -
MAX_FREQ建议设为 1000 左右,高频访问统一归入“高频桶”,避免min_freq无限递增 - 这个 while 循环只在
evict()入口执行一次,均摊仍是 O(1),因为每个频次最多被检查一次
Node 结构里必须带 freq 字段,且不能靠链表位置反推
有人试图省字段,认为“节点在哪个桶里,它的频次就是桶的 key”,这是错的:
- 节点可能正被移动中(
get过程中先删后插),此时它既不在旧桶也不在新桶,freq字段是唯一可信来源 - 桶复用场景下(如把
freq=5桶清空后用于freq=10),链表位置和频次彻底脱钩 -
min_freq更新逻辑依赖node.freq值来比对old_freq == min_freq,缺它就断链
正确结构至少含:int key、int value、int freq、std::list<node>::iterator it</node>(若用 std::list)或 Node* prev/Node* next(若手写链表)。
最易被忽略的一点:所有对 freq_to_list 的修改(erase、insert、clear)都必须与 min_freq 检查严格配对,漏一次,evict() 就可能崩在生产环境里,而且很难复现。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










