当前位置: 首页 > news >正文

LRU缓存淘汰算法详解与C++实现

文章目录

    • 算法概述
    • 核心思想
    • 数据结构设计
      • 1. 双向链表 (std::list)
      • 2. 哈希表 (std::unordered_map)
    • 代码实现解析
      • 关键方法详解
        • 1. 数据查询方法 `get()`
        • 2. 数据插入/更新方法 `put()`
      • 3. 调试输出方法
    • 时间复杂度分析
    • 关键技术与优化
      • 1. 移动语义优化
      • 2. 链表节点移动
      • 3. 迭代器稳定性
    • 应用场景
      • 1. 数据库缓存
      • 2. 操作系统
      • 3. Web服务
      • 4. 编程框架
    • 完整测试示例
    • 总结

算法概述

LRU(Least Recently Used)最近最少使用算法是一种常用的缓存淘汰策略。当缓存空间不足时,它会优先淘汰最久未被访问的数据,保留最近被访问的数据。

核心思想

LRU算法的核心思想是:如果一个数据在最近一段时间没有被访问到,那么它在将来被访问的可能性也很小。基于这种"局部性原理",LRU算法将最近访问的数据放在缓存中,淘汰长时间未被访问的数据。

数据结构设计

1. 双向链表 (std::list)

  • 作用:维护数据的访问顺序
  • 特点
    • 头部表示最近访问的数据(MRU - Most Recently Used)
    • 尾部表示最久未访问的数据(LRU - Least Recently Used)
    • 支持在任意位置快速插入和删除(O(1)时间复杂度)

2. 哈希表 (std::unordered_map)

  • 作用:提供快速的数据查找能力
  • 特点
    • 键(Key):缓存数据的键
    • 值(Value):指向链表中对应节点的迭代器
    • 查找时间复杂度:O(1)

代码实现解析

#include <iostream>
#include <unordered_map>
#include <list>template<typename K, typename V>
class LRUCache {
public:// 构造函数,初始化缓存容量explicit LRUCache(size_t capacity) : cap_(capacity) {}// 禁用拷贝构造和赋值操作LRUCache(const LRUCache&) = delete;LRUCache& operator=(const LRUCache&) = delete;private:size_t cap_;  // 缓存容量std::list<std::pair<K, V>> item_;  // 双向链表,存储键值对std::unordered_map<K, typename std::list<std::pair<K, V>>::iterator> index_;  // 哈希索引
};

关键方法详解

1. 数据查询方法 get()
bool get(const K& key, V& out) {// 在哈希表中查找键auto it = index_.find(key);if (it == index_.end()) {return false;  // 未找到,缓存未命中}// 缓存命中:获取值并移动到链表头部out = it->second->second;item_.splice(item_.begin(), item_, it->second);return true;
}

工作流程

  1. 在哈希表中查找键是否存在
  2. 如果不存在,返回 false(缓存未命中)
  3. 如果存在:
    • 通过迭代器获取对应的值
    • 使用 splice() 方法将节点移动到链表头部
    • 返回 true(缓存命中)
2. 数据插入/更新方法 put()
void put(const K& key, V value) {auto it = index_.find(key);if (it != index_.end()) {// 键已存在:更新值并移动到头部it->second->second = std::move(value);item_.splice(item_.begin(), item_, it->second);return;}// 键不存在:检查是否需要淘汰数据if (item_.size() == cap_) {// 缓存已满,淘汰尾部数据auto& old = item_.back();index_.erase(old.first);   // 从哈希表删除item_.pop_back();          // 从链表删除}// 插入新数据到头部item_.emplace_front(key, std::move(value));index_[item_.front().first] = item_.begin();  // 更新哈希表索引
}

工作流程

  1. 检查键是否已存在
  2. 如果存在:更新值并移动到链表头部
  3. 如果不存在:
    • 检查缓存是否已满
    • 如果已满,淘汰链表尾部的数据(从链表和哈希表中删除)
    • 将新数据插入链表头部
    • 在哈希表中建立新索引

3. 调试输出方法

void debug_print() {std::cout << "[MRU->LRU]";for (auto& p : item_) {std::cout << "(" << p.first << "," << p.second << ")";}std::cout << std::endl;
}

时间复杂度分析

操作时间复杂度说明
查询 (get)O(1)哈希表查找 + 链表节点移动
插入/更新 (put)O(1)哈希表查找 + 可能的淘汰 + 链表插入
空间复杂度O(n)n为缓存容量

关键技术与优化

1. 移动语义优化

it->second->second = std::move(value);
item_.emplace_front(key, std::move(value));

使用 std::move() 避免不必要的拷贝操作,提高性能。

2. 链表节点移动

item_.splice(item_.begin(), item_, it->second);

std::list::splice() 可以在常数时间内将节点从一个位置移动到另一个位置,这是实现LRU算法的关键。

3. 迭代器稳定性

std::list 的迭代器在插入和删除操作(除了被删除的元素)后仍然保持有效,这保证了哈希表中存储的迭代器的有效性。

应用场景

1. 数据库缓存

  • MySQL的查询缓存
  • Redis的键值缓存

2. 操作系统

  • 页面置换算法
  • 文件系统缓存

3. Web服务

  • HTTP缓存
  • CDN内容分发

4. 编程框架

  • 线程池任务缓存
  • 连接池管理

完整测试示例

int main() {// 创建容量为3的LRU缓存LRUCache<int, std::string> cache(3);// 插入初始数据cache.put(1, "one");cache.put(2, "two");cache.put(3, "three");cache.debug_print();  // 输出: [MRU->LRU](3,three)(2,two)(1,one)// 查询数据(会改变访问顺序)std::string out;if (cache.get(2, out)) {std::cout << "get 2:" << out << std::endl;  // 输出: get 2:two}cache.debug_print();  // 输出: [MRU->LRU](2,two)(3,three)(1,one)// 插入新数据(触发淘汰)cache.put(4, "four");cache.debug_print();  // 输出: [MRU->LRU](4,four)(2,two)(3,three)return 0;
}

总结

LRU缓存算法通过结合哈希表的快速查找和双向链表的顺序维护,实现了高效的缓存管理。这种设计模式在需要快速访问最近使用数据的场景中非常有用,是现代计算机系统中不可或缺的基础组件之一。

优点

  • 时间复杂度优秀(O(1))
  • 符合局部性原理
  • 实现相对简单

缺点

  • 需要维护额外的数据结构
  • 内存开销较大(哈希表+链表)
  • 对于某些特殊访问模式可能不是最优
http://www.dtcms.com/a/605286.html

相关文章:

  • AbMole小讲堂丨Cyclophosphamide(环磷酰胺):应用于肿瘤与免疫研究的热门烷化工具
  • 网站建设费用如何收取什么叫网站开发应用框架
  • 怎么在.Net中做团购网站专门做钻石国外网站
  • 教程上新丨Deepseek-OCR 以极少视觉 token 数在端到端模型中实现 SOTA
  • Mac多功能音视频AI处理工具VideoProc Converter AI
  • 【技术贴】全链路协同!艾为电子开启端侧AI音频“精而优”时代
  • 2025国产ITSM厂商选型指南:从基础流程、智能赋能到全链路协同方案的全面对比
  • 数据结构——四十二、二叉排序树(王道408)
  • VueUse的使用
  • 【LeetCode】111. 二叉树的最小深度
  • 如何将html发布到网站wordpress用户筛选
  • 深度智能体-智能体加强版
  • ZCC75XXH- 40V/150mA 高压线性稳压器替代HT75XX
  • 多媒体语音通话中,信令参数T1/ms, T2/s, T4/s作用
  • Travel uni-app 项目说明
  • 永磁同步电机无速度算法--基于一阶线性状态观测器的反电动势观测器
  • 番禺网站建设怎样网站建设公司怎样做账
  • 网站开发项目总结模板网站开发 证书
  • Python 自定义迭代器 --以斐波那契数列为例
  • AI一键PPT 2.0.3 一键智能生成
  • 232. 用栈实现队列
  • 如何在桌面创建网页快捷图标?(电脑 / 手机通用操作指南)
  • soular实战教程系列(2) - 如何统一管理TikLab帐号体系
  • k8s 发行说明(版本)
  • 批处理病毒原理、防御
  • 网站建设对企业的好处有哪些嘉兴网站制作建设
  • 几大网站类型网站建设所需人员
  • KAFKA自动修改所有以**开头的主题脚本
  • 记录生活系统|记录美好|健康管理|基于java+Android+微信小程序的记录生活系统设计与实现(源码+数据库+文档)
  • PHP Calendar