c++中如何实现LRU缓存淘汰算法_c++ LRU Cache设计思路
发布时间 - 2026-01-08 00:00:00 点击率:次用 std::list + std::unordered_map 可实现 O(1) LRU 缓存:unordered_map 提供 key→list 迭代器映射以支持快速查找,list 支持头插、尾删和指定位置删除,共同满足 get/put/淘汰的常数时间要求。
用 std::list + std::unordered_map 实现 O(1) LRU 缓存
LRU 缓存的核心难点是:既要快速查找(get),又要快速移动到最近使用位置(put更新顺序),还要在容量满时删掉最久未用项。C++ 标准库里,std::list 支持常数时间的节点删除和头/尾插入,std::unordered_map 提供 O(1) 的 key 查找 —— 二者组合刚好满足需求。
关键设计点:std::unordered_map 存 key → iterator(指向 std::list 中对应 pair 的迭代器),这样 get 时能直接定位并移到链表头部;put 时若已存在,就用迭代器擦除原节点再重新插入头部;若超容,则删掉链表尾部节点,并从 map 中移除对应 key。
注意:std::list::erase(iterator) 是合法且高效的,但不能保存失效迭代器 —— 每次 erase 后原迭代器立即无效,所以必须先用 map[key] 获取、操作、再更新 map。
std::list 节点值类型选 std::pair 还是自定义结构体?
用 std::pair 最简洁,但可读性差;用结构体(如 struct CacheNode { int key; int val; };)更清晰,尤其当未来要扩展字段(比如访问时间戳、引用计数)时。实际性能无差异 —— std::list 存的是对象副本,两者都是 trivial 类型,移动开销一致。
实操建议:
- 初期用
std::pair快速验证逻辑 - 上线前或需维护时,换成命名明确的结构体,避免
it->first/it->second带来的歧义 - 不要用
std::vector或std::deque替代std::list:前者不支持 O(1) 中间删除,后者不保证迭代器在插入/删除后稳定
如何处理 put 时 key 已存在但 value 不同的情况?
LRU 规范要求:put(k, v) 若 k 已存在,应更新 value 并将该节点移到最近使用位置(即链表头),而不是忽略或报错。常见错误是只更新 map 中的 value,却忘了调整 list 顺序 —— 这会导致缓存淘汰逻辑错乱,最久未用项可能不是真实最久的。
正确做法(以 std::pair 为例):
if (cache_map.find(key) != cache_map.end()) {
auto it = cache_map[key];
cache_list.erase(it); // 先删旧位置
}
cache_list.push_front({key, value});
cache_map[key] = cache_list.begin(); // 更新 map 中的迭代器
注意:push_front 返回新节点的迭代器,必须立刻存入 map;如果先 erase 再 push_front 但没更新 map,后续 get 会解引用已失效迭代器,触发未定义行为。
为什么不用 std::map 替代 std::unordered_map?
std::map 是红黑树,查找/插入/删除都是 O(log n),而 LRU 要求所有核心操作 O(1),用它会让整体复杂度退化为 O(log n),尤其在高并发或大数据量场景下明显拖慢。除非你明确需要 key 有序遍历(LRU 本身不需要),否则没有理由选 std::map。
另一个坑:std::unordered_map 的迭代器在 rehash 时会失效(例如插入导致扩容)。但只要你不保存跨操作的迭代器(即每次 get/put 都现场查 map 取迭代器),就完全安全 —— 因为 cache_map[key] 总返回当前有效迭代器。
补充:C++20 起可考虑 std::unordered_map::extract 避免拷贝,但对 int 这种小类型意义不大;真正受益的是带大成员的对象缓存。
最易被忽略的一点:std::list::iterator 不能序列化或跨线程共享,如果你的缓存要用于多线程环境,必须加锁(如 std::shared_mutex),且锁粒度要覆盖“查 map → 操作 list → 更新 map”整个临界区,不能只锁其中一步。
# node
# 大数据
# ai
# c++
# 标准库
# 为什么
# red
# 结构体
# int
# 值类型
# Struct
# 线程
# 多线程
# map
# 并发
# 对象
# 算法
# 迭代
# 的是
# 都是
# 最久
# 链表
# 移到
# 快速查找
# 不需要
# 遍历
# 要在
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
Laravel Debugbar怎么安装_Laravel调试工具栏配置指南
如何彻底卸载建站之星软件?
如何自定义建站之星网站的导航菜单样式?
Laravel怎么使用artisan命令缓存配置和视图
香港服务器租用费用高吗?如何避免常见误区?
laravel服务容器和依赖注入怎么理解_laravel服务容器与依赖注入解析
javascript中闭包概念与用法深入理解
利用python获取某年中每个月的第一天和最后一天
Google浏览器为什么这么卡 Google浏览器提速优化设置步骤【方法】
Laravel如何使用Seeder填充数据_Laravel模型工厂Factory批量生成测试数据【方法】
高端建站如何打造兼具美学与转化的品牌官网?
ChatGPT怎么生成Excel公式_ChatGPT公式生成方法【指南】
如何快速搭建个人网站并优化SEO?
Laravel如何使用Service Container和依赖注入?(代码示例)
javascript基本数据类型及类型检测常用方法小结
成都品牌网站制作公司,成都营业执照年报网上怎么办理?
米侠浏览器网页背景异常怎么办 米侠显示修复
Laravel怎么配置自定义表前缀_Laravel数据库迁移与Eloquent表名映射【步骤】
网易LOFTER官网链接 老福特网页版登录地址
Laravel如何实现数据库事务?(DB Facade示例)
PHP 实现电台节目表的智能时间匹配与今日/明日轮播逻辑
EditPlus中的正则表达式实战(6)
如何在腾讯云服务器上快速搭建个人网站?
详解Oracle修改字段类型方法总结
HTML5打空格有哪些误区_新手常犯的空格使用错误【技巧】
专业企业网站设计制作公司,如何理解商贸企业的统一配送和分销网络建设?
昵图网官方站入口 昵图网素材图库官网入口
千库网官网入口推荐 千库网设计创意平台入口
Laravel如何操作JSON类型的数据库字段?(Eloquent示例)
Linux系统命令中screen命令详解
谷歌Google入口永久地址_Google搜索引擎官网首页永久入口
JavaScript如何实现继承_有哪些常用方法
Laravel如何使用查询构建器?(Query Builder高级用法)
弹幕视频网站制作教程下载,弹幕视频网站是什么意思?
网站图片在线制作软件,怎么在图片上做链接?
Laravel Eloquent性能优化技巧_Laravel N+1查询问题解决
Win11怎么查看显卡温度 Win11任务管理器查看GPU温度【技巧】
如何在万网利用已有域名快速建站?
js实现点击每个li节点,都弹出其文本值及修改
html文件怎么打开证书错误_https协议的html打开提示不安全【指南】
JS经典正则表达式笔试题汇总
用yum安装MySQLdb模块的步骤方法
微信小程序 canvas开发实例及注意事项
百度浏览器网页无法复制文字怎么办 百度浏览器复制修复
合肥制作网站的公司有哪些,合肥聚美网络科技有限公司介绍?
免费的流程图制作网站有哪些,2025年教师初级职称申报网上流程?
Laravel如何集成第三方登录_Laravel Socialite实现微信QQ微博登录
悟空识字怎么关闭自动续费_悟空识字取消会员自动扣费步骤
Win11应用商店下载慢怎么办 Win11更改DNS提速下载【修复】
Laravel怎么配置S3云存储驱动_Laravel集成阿里云OSS或AWS S3存储桶【教程】
下一篇:XP系统开机后桌面无图标怎么办
下一篇:XP系统开机后桌面无图标怎么办


同满足 get/put/淘汰的常数时间要求。