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

C++手撕LRU

背景

        小H最近在准备面试,发现手撕的环节除了一些算法部分还有偏工程部分,而其中手撕LRU又是比较常见的,所以决定整理一下。

LRU

        LRU(Least Recently Used,最近最少使用) 是一种常见的缓存淘汰策略,用于在缓存空间不足时,决定哪些数据应该被移除,以腾出空间存储新数据。其核心思想是:如果一条数据最近很少被访问,那么未来被访问的概率也很低,因此优先淘汰这类数据

核心原理

        当缓存达到最大容量时,LRU 策略会筛选出最近一次访问时间最早(即最久未被使用) 的数据,将其从缓存中删除,然后存入新数据。

举例来说,假设缓存容量为 3,数据访问顺序如下:

  1. 访问 A → 缓存:[A](最近使用:A)
  2. 访问 B → 缓存:[A, B](最近使用:B)
  3. 访问 C → 缓存:[A, B, C](最近使用:C)
  4. 访问 B → 缓存:[A, C, B](最近使用:B,B 被重新访问,位置更新)
  5. 访问 D(缓存满)→ 淘汰最久未用的 A → 缓存:[C, B, D](最近使用:D)

实现方式

LRU 缓存需要高效支持两种操作:

  • 访问数据(get):若数据在缓存中,需将其标记为 “最近使用”;若不在,返回未命中。
  • 插入数据(put):若缓存未满,直接插入并标记为 “最近使用”;若已满,先删除最久未用数据,再插入新数据。

常见的实现结构是 “哈希表 + 双向链表”

  • 双向链表:按访问时间排序,头部存放最近使用(MRU)的数据,尾部存放最久未用(LRU)的数据。
  • 哈希表:键为数据的键,值为双向链表中对应节点的指针,用于 O (1) 时间复杂度定位数据。

        这种组合可使 get 和 put 操作的时间复杂度均为 O (1),本次双向链表我们也是自己实现,没有直接使用List,哈希表就直接使用unordered_map就行了。

struct ListNode {int key;int value;ListNode* prev;ListNode* next;ListNode(int k,int v):key(k),value(v),prev(nullptr),next(nullptr){}
};class DoublyLinkedList{
public:ListNode* head;ListNode* tail;DoublyLinkedList(){head = new ListNode(0,0);tail = new ListNode(0,0);head->next = tail;tail->prev = head;}~DoublyLinkedList(){clear();delete head;delete tail;}// 清空链表void clear(){ListNode* now = head->next;while(now != tail){ListNode* nextNode = now->next;delete now;now = nextNode;}head->next = tail;tail->prev = head;}void push_front(ListNode* node){node->next = head->next;node->prev = head;head->next->prev = node;head->next = node;}void erase(ListNode* node){node->prev->next = node->next;node->next->prev = node->prev;}void move_to_front(ListNode* node){node->prev->next = node->next;node->next->prev = node->prev;push_front(node);}ListNode* pop_back() {ListNode *node = tail->prev;if (node == head) return nullptr;// 断开连接但是不删除节点node->prev->next = tail;tail->prev = node->prev;node->prev = nullptr;node->next = nullptr;return node;}
};class LRUCache{
public:LRUCache(int capacity):_capacity(capacity){}int get(int key){auto it=_cache.find(key);if(it == _cache.end()){return -1;}_list.move_to_front(it->second);return it->second->value;}void put(int key,int value){auto it=_cache.find(key);if(it != _cache.end()){it->second->value=value;_list.move_to_front(it->second);}else{if(_cache.size()==_capacity){ListNode* node = _list.pop_back();if(node!= nullptr){_cache.erase(node->key);delete node;}}ListNode* newNode = new ListNode(key,value);_list.push_front(newNode);_cache[key] = newNode;}}private:int _capacity;DoublyLinkedList _list;std::unordered_map<int,ListNode*> _cache;
};

        代码上没有太复杂的地方,主要就是关注一下释放节点的时候前后指针如何变换、还有满载、访问的策略。

        附上一份测试代码。

void test()
{LRUCache cache(2);cache.put(1, 1); // 缓存现在为 {1=1}cache.put(2, 2); // 缓存现在为 {1=1, 2=2}std::cout << "Get 1: " << cache.get(1) << std::endl; // 返回 1cache.put(3, 3); // 缓存达到容量,移除最近最少使用的键 2,缓存现在为 {1=1, 3=3}std::cout << "Get 2: " << cache.get(2) << std::endl; // 返回 -1(未找到)cache.put(4, 4); // 缓存达到容量,移除最近最少使用的键 1,缓存现在为 {3=3, 4=4}std::cout << "Get 1: " << cache.get(1) << std::endl; // 返回 -1(未找到)std::cout << "Get 3: " << cache.get(3) << std::endl; // 返回 3std::cout << "Get 4: " << cache.get(4) << std::endl; // 返回 4
}

结语

        如果本章内容上有什么问题,可以与作者联系。

http://www.dtcms.com/a/343496.html

相关文章:

  • 中国之路 向善而行 第三届全国自驾露营旅游发展大会在阿拉善启幕
  • Webpack的使用
  • 5.Shell脚本修炼手册---Linux正则表达式(Shell三剑客准备启动阶段)
  • AI 时代的 “人机协作”:人类与 AI 如何共塑新生产力
  • 7.Shell脚本修炼手册---awk基础入门版
  • camel中支持的模型与工具
  • 爬虫基础学习-POST方式、自定义User-Agent
  • FCN网络结构讲解与Pytorch逐行讲解实现
  • 小程序个人信息安全检测技术:从监管视角看加密与传输合规
  • 限流技术:从四大限流算法到Redisson令牌桶实践
  • SpringBoot整合HikariCP数据库连接池
  • 机器学习聚类算法
  • 【机器学习】线性回归
  • 深入解析C++非类型模板参数
  • Linux入门DAY29
  • AI 产业落地:从 “实验室神话” 到 “车间烟火气” 的跨越
  • 【TrOCR】模型预训练权重各个文件解读
  • SpringAI1.0.1实战教程:避坑指南25年8月最新版
  • 近端策略优化算法PPO的核心概念和PyTorch实现详解
  • Typescript入门-函数讲解
  • 创建一个springboot starter页面
  • LG P2617 Dynamic Rankings Solution
  • 1688 商品详情接口数据全解析(1688.item_get)
  • 关于从零开始写一个TEE OS
  • 如何安装 VMware Workstation 17.5.1?超简单步骤(附安装包下载)
  • Building Systems with the ChatGPT API 使用 ChatGPT API 搭建系统(第四章学习笔记及总结)
  • 一文讲清楚:场景、痛点、需求
  • mainMem.useNamedFile = “FALSE“ 的效果
  • UE5多人MOBA+GAS 52、下载源码构建引擎
  • 如何处理项目中棘手的依赖版本冲突问题