不能只用std::map或std::list单独实现lru缓存:map支持o(log n)查找但无法o(1)删除最久未用项,list支持o(1)头尾操作但查找为o(n);需组合二者——map存key到list迭代器的映射,list存键值对,利用list迭代器不失效特性实现o(1)定位与顺序维护。

为什么不能只用 std::map 或只用 std::list
单独用 std::map 可以 O(log n) 查找和插入,但无法 O(1) 删除最久未用项;单独用 std::list 虽然能 O(1) 移动节点到头部、O(1) 弹出尾部,但查找某个 key 对应的节点要 O(n)。面试官想看到的是对「时间局部性」和「数据结构组合优势」的理解——map 提供快速定位,list 提供顺序维护,二者缺一不可。
std::list 存什么?std::map 的 value 又该存什么?
常见错误是让 map 的 value 存 int 或 std::pair<int int></int>,这会导致删除时无法反向定位 list 中的迭代器。正确做法是:
-
std::list存std::pair<const key value></const>(比如std::pair<int int></int>),保证节点内容完整 -
std::map<key std::list>::iterator></key>的 value 必须是list的迭代器,这样才能在 O(1) 内擦除对应节点 - 注意:
std::list迭代器不会因其他元素增删而失效,这是关键保障
get() 和 put() 操作中如何同步更新 list 顺序?
核心逻辑不是“移动节点”,而是“删旧插新”——因为 std::list 不支持原地修改迭代器指向的位置。每次访问或写入,都要:
- 若 key 已存在:
map查到对应list迭代器 → 用list.splice()把该节点移到list开头(O(1)) - 若 key 不存在(put):
list.push_front()新节点,再把新迭代器存入map - 容量超限时:
list.back()取最久未用项 → 从map中 erase 其 key →list.pop_back()
别手写遍历删除;splice() 是唯一安全高效的移动方式。
立即学习“C++免费学习笔记(深入)”;
有哪些隐蔽的坑?
面试写出来跑不过,大概率栽在这几处:
-
std::list::iterator不能直接解引用后取.first,必须先确认迭代器有效(尤其 erase 后又用了旧迭代器) - put 时若 key 已存在,要先用
map.at(key)或map[key]获取旧迭代器,再list.erase()它,否则重复插入导致 size 失控 - 不要用
auto it = list.begin()后反复 ++,容易越界;统一用list.front()/list.back()配合splice() - C++11 起
std::list的splice()第一个参数是容器本身,别漏传:cache_list.splice(cache_list.begin(), cache_list, it)
最易被忽略的是:当 put 替换已有 key 时,必须先从 map 中 erase 旧映射,再插入新迭代器,否则 map 里残留的旧迭代器会指向已被 erase 的节点——接下来任何解引用都是未定义行为。










