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

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.xdnf.cn/news/18315.html

相关文章:

  • RocketMQ 消息消费 单个消费和批量消费配置实现对比(Springboot),完整实现示例对比
  • 链表-143.重排链表-力扣(LeetCode)
  • SQL视图、存储过程和触发器
  • npm全局安装后,cmd命令行可以访问,vscode访问报错
  • Django REST框架核心:GenericAPIView详解
  • GitHub Push 认证失败 fatal Authentication failed
  • OceanBase 分区裁剪(Partition Pruning)原理解读
  • Binlog Server守护MySQL数据0丢失
  • 基于Pytochvideo训练自己的的视频分类模型
  • python中view把矩阵维度降低的时候是什么一个排序顺序
  • 机器学习——数据清洗
  • 【论文阅读】Multi-metrics adaptively identifies backdoors in Federated Learning
  • Linux 文本处理与 Shell 编程笔记:正则表达式、sed、awk 与变量脚本
  • 本地文件上传到gitee仓库的详细步骤
  • Excel表格复制到word中格式错乱
  • Nginx 的完整配置文件结构、配置语法以及模块详解
  • 【学习笔记】大话设计模式——一些心得及各设计模式思想记录
  • Vue3全局配置Loading的完整指南:从基础到实战
  • PyTorch API 4
  • Mac 4步 安装 Jenv 管理多版本JDK
  • Linux Capability 解析
  • strncpy 函数使用及其模拟实现
  • 为什么我的UI界面会突然卡顿,失去响应
  • 安装使用Conda
  • pyqt 的自动滚动区QScrollArea
  • Rust 入门 包 (二十一)
  • Ubuntu 虚拟显示器自动控制服务设置(有无显示器的切换)
  • 华为数通认证学习
  • 微算法科技(NASDAQ: MLGO)引入高级区块链DSR算法:重塑区块链网络安全新范式
  • K8S-Configmap资源