LRU Cache缓存替换算法

目录

一、LRU 是什么?Cache是什么?

二、LRU Cache的实现

三、源码


一、LRU 是什么?Cache是什么?

LRU 是 "Least Recently Used" 的缩写,意思是“最近最少使用”。它是一种常用的 缓存(Cache)替换算法

缓存(Cache)就像一个临时的“中转站”或“快速工作台”,它的作用是解决两个速度差异很大的设备之间数据交换的“瓶颈”问题

例如

  • CPU 和内存之间: 我们电脑的 CPU 速度非常快,而主内存(通常用 DRAM 技术)相对较慢。为了不让 CPU 总是“等”内存,就在它们之间加了一个高速缓存(通常用更快的 SRAM 技术)。CPU 会优先从这个高速缓存里找数据,大大提升了效率。
  • 内存和硬盘之间:硬盘速度比内存慢得多,操作系统会在内存里开辟一块区域作为硬盘的缓存,存放最近读写过的数据。
  • 硬盘和网络之间: 当你浏览网页时,浏览器会把看过的图片、网页文件等暂时保存在硬盘上的 “Internet 临时文件夹”或“网络缓存” 里。下次再访问同一个网站,浏览器就可以直接从本地缓存加载,而不必每次都从慢速的网络重新下载,让网页打开更快。

总之,LRU 是管理缓存空间的一种策略(当缓存满了,优先淘汰最久没被用过的数据)。而缓存本身,则是解决不同速度设备间协作效率问题的关键设计,存在于计算机系统的多个层级。Cache的容量有限,因此当Cache的容量用完后,而又有新的内容需要添加进来时, 就需要挑选 并舍弃原有的部分内容,从而腾出空间来放新内容。LRU Cache 的替换原则就是将最近最少使用 的内容替换掉。其实,LRU译成最久未使用会更形象, 因为该算法每次替换掉的就是一段时间内 最久没有使用过的内容。

二、LRU Cache的实现

实现LRU Cache的方法和思路很多,但是要保持高效实现O(1)的put和get,且其涉及到两个核心问题:快速访问数据维护数据的使用顺序,那么使用双向链表和 哈希表的搭配是最高效和经典的。使用双向链表是因为双向链表可以实现任意位置O(1)的插入和 删除(维护数据使用顺序),使用哈希表是因为哈希表的增删查改也是O(1)和快速访问。

算法的思想:缓存容量有限,当缓存满了时,优先淘汰最久未被访问的数据,保留最近使用过的数据。 

核心的数据结构

  1. 哈希表:用于 O(1) 时间 快速查询、插入、删除键值对。
  2. 双向链表:用于维护数据的访问顺序
    • 最近访问的或者新插入的放在链表头部
    • 最久未访问的放在链表尾部
    • 当缓存满时,直接删除链表尾部的数据。

 接下来借助一个 OJ 题来帮助实现 LRU Cache 算法。题目描述、示例提示如下

 

题目分析:

题目中要求函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。

题目要求我们实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。使用双向链表和哈希表的搭配是最高效和经典的。

  //hash做到查找更新/插入是O(1)unordered_map<int, LtIter> _hashmap;//LRU 默认链表尾部的是最久未被使用的list<pair<int, int>> _LRUList;

 但是只有这些是远远不够的,更新的时候其实复杂度是O(N),更新的情况就是调用put先在哈希表里面查找到key是已存在的,那然后我们要修改,哈希表里面我找到这个就可以直接修改。 但是,在list里也要修改,因为我们替换的时候找最久未被使用的那个值就是要从list里面找。 但是要修改list的话我们知不知道当前要修改的这个元素是list里面的哪一个元素? 是不知道的,所以还得遍历list去找。找到之后更新一下,然后把它移到头部去,更新顺序。

所以接下来我们就需要一个设计:
还是list和unordered_map搭配。 list里面呢还是存key-value的键值对pair。然后哈希表里面key还是要存的,但是不再像上面写的那样直接存key对应的数据value,而是存这个key对应的元素在list里面的对应的迭代器。(那这样真正的数据就只存在list里面)

那这样的话如果更新的话,首先我们在哈希表里面找到key,然后通过它里面存的该元素在list中的迭代器,就可以直接修改list里面存放的数据。

private:typedef list<pair<int,int>>::iterator LtIter;//hash做到查找更新/插入是O(1)unordered_map<int, LtIter> _hashmap;//LRU 默认链表尾部的是最久未被使用的list<pair<int, int>> _LRUList;size_t _capacity;//缓存的容量控制,当缓存大小超过此值时,需要淘汰最久未使用的元素

构造函数:

class LRUCache {
public:LRUCache(int capacity):_capacity(capacity){}

get() 方法实现:

int get(int key) {auto ret=_hashmap.find(key);if(ret!=_hashmap.end()){LtIter it=ret->second;//获取哈希表中存储的链表迭代器//将it对应的结点转移到链表头部_LRUList.splice(_LRUList.begin(),_LRUList,it);//操作是 O(1) 时间复杂度的,因为它只修改指针而不需要复制数据return it->second->second;}return -1;
}

上面的splice接口功能如下:它可以把一个链表的一部分转移到另一个链表(当然也可以是同一个链表直接进行转移) 所以我们就可以直接调用splice将这个结点转移到list的头部。 

put()接口的实现:

put的话呢无非就两种操作 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。 当然插入的时候需要判断: 如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字,然后插入新值。 另外不论是插入还是更新,都应该把插入或更新的值放到链表头部。

void put(int key, int value) {auto ret=_hashmap.find(key);//如果找到,更新值if(ret!=_hashmap.end()){LtIter it=ret->second;it->second=value;//将it对应的结点转移到链表头部_LRUList.splice(_LRUList.begin(),_LRUList,it);}else//找不到,插入{//如果满了需要先删除最久未使用的值if(_capacity==_hashmap.size()){pair<int,int> back=_LRUList.back();_hashmap.erase(back.first);_LRUList.pop_back();}//插入_LRUList.push_front(make_pair(key,value));_hashmap[key]=_LRUList.begin();}
}

三、源码

class LRUCache {
public:LRUCache(int capacity):_capacity(capacity){}int get(int key) {auto ret=_hashmap.find(key);if(ret!=_hashmap.end()){LtIter it=ret->second;//将it对应的结点转移到链表头部_LRUList.splice(_LRUList.begin(),_LRUList,it);return it->second;}return -1;}void put(int key, int value) {auto ret=_hashmap.find(key);//如果找到,更新值if(ret!=_hashmap.end()){LtIter it=ret->second;it->second=value;//将it对应的结点转移到链表头部_LRUList.splice(_LRUList.begin(),_LRUList,it);}else//找不到,插入{//如果满了需要先删除最久未使用的值if(_capacity==_hashmap.size()){pair<int,int> back=_LRUList.back();_hashmap.erase(back.first);_LRUList.pop_back();}//插入_LRUList.push_front(make_pair(key,value));_hashmap[key]=_LRUList.begin();}}
private:typedef list<pair<int,int>>::iterator LtIter;//hash做到查找更新/插入是O(1)unordered_map<int, LtIter> _hashmap;//LRU 默认链表尾部的是最久未被使用的list<pair<int, int>> _LRUList;size_t _capacity;
};

感谢阅读! 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/92371.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/92371.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

自定义视图:图形与图像的处理(二):绘图

除了使用已有的图片之外&#xff0c;Android应用还常常需要在运行时动态地生成图片&#xff0c;比如一个手机游戏&#xff0c;游戏界面看上去丰富多彩&#xff0c;而且可以随着用户动作而动态改变&#xff0c;这就需要借助于Android的绘图支持了。1. Android绘图基础:Canvas、P…

微服务、服务网格、Nacos架构与原理

Nacos架构与原理 -服务网格生态-阿里云开发者社区 ------ 该文章用于学习参考,如有侵权,请直接联系下架 服务网格的核心职责:治理“服务通信” 包括但不限于: 功能 举例说明 负载均衡 动态选择服务实例 熔断、重试 某个服务失败时自动切换、重试 流量路由 灰度发布、蓝绿…

STM32——启动过程浅析

总&#xff1a;STM32——学习总纲 参考文件&#xff1a; STM32 MAP文件浅析-V1.1 STM32 启动文件浅析_V1.2 Cortex-M3权威指南(中文)、ARM Cotrex-M3权威指南(英文).zip 一、Map文件解析 1.1 MDK编译过程文件 在编译中&#xff0c;会生成11种编译过程文件&#xff0c;可…

区块链简介

一、区块链简介 狭义上的定义&#xff1a; 区块链是一种链式数据结构&#xff0c;通过按时间顺序将数据块逐一连接形成。这种结构通过密码学确保了数据的不可篡改性和不可伪造性&#xff0c;形成了一种分布式账本技术。 广义上的定义&#xff1a; 区块链技术不仅仅是一种数据…

NestJS中@Injectable装饰器

一、基础定义与核心作用 1.1 什么是Injectable&#xff1f; Injectable() 是 NestJS 依赖注入&#xff08;Dependency Injection, DI&#xff09;系统的核心装饰器&#xff0c;用于将类标记为可注入的提供者&#xff08;Provider&#xff09;。它告知 NestJS 的 IoC&#xff08…

【机器学习深度学习】大模型应用落地:微调与RAG的角色与实践

目录 前言 一、微调与RAG&#xff1a;大模型应用落地的两大支柱 1. 微调&#xff08;Fine-tuning&#xff09; 2. RAG&#xff08;Retrieval-Augmented Generation&#xff09; 二、微调可以做什么&#xff1f; 1. 模型自我认知调整 2. 对话风格优化 3. 提升问题理解能…

List、ArrayList 与顺序表

目录 一、List 介绍 二、线性表 三、自己实现 ArrayList 3.1 显示元素 3.2 增 3.2.1 默认在数组后面新增元素 3.2.2 在指定位置中新增元素 3.3 查 3.4 取值 3.5 改 3.5.1 把 pos 位置的元素修改成 value 3.5.2 删除某个元素 3.5.3 清空 四、认识 ArrayList 4.0 说…

Baumer工业相机堡盟工业相机如何通过YoloV8深度学习模型实现各类垃圾的分类检测识别(C#代码UI界面版)

Baumer工业相机堡盟工业相机如何通过YoloV8深度学习模型实现各类垃圾的分类检测识别&#xff08;C#代码UI界面版&#xff09;工业相机使用YoloV8模型实现各类垃圾的分类检测识别工业相机通过YoloV8模型实现各类垃圾的分类检测识别的技术背景在相机SDK中获取图像转换图像的代码分…

EasyExcel高效工具类:简化Excel导入导出,支持多Sheet与枚举转换

文章目录前言一、依赖坐标二、工具类&#xff1a;ExcelUtil三、测试1.实体类2.前置操作3.单Sheet导出4.单Sheet导入5.多Sheet导出6.多Sheet导入7.完整代码四、扩展&#xff1a;自定义注解实现枚举类型转换1.枚举接口2.枚举类3.注解4.转换类5.使用示例6.测试总结前言 在现代应用…

技术速递|GitHub Copilot for Eclipse 迈出重要一步

我们非常高兴地宣布&#xff1a;2025 年 7 月 22 日&#xff0c;GitHub Copilot for Eclipse 又迈出了重要一步&#xff0c;Eclipse 变得更智能、更快捷&#xff0c;而且与 Eclipse 的集成也更无缝了&#xff01;这是继新功能上线以来&#xff0c;又一次质的提升。 &#x1f…

Coze Loop:开源智能体自动化流程编排平台原理与实践

项目简介 Coze Loop 是 Coze 团队开源的智能体自动化流程编排平台。它以“Loop”为核心概念,支持开发者通过低代码/可视化方式,将多种 AI Agent、插件、API、数据流等灵活编排为自动化工作流,实现复杂的智能体协作、任务自动化和多模态数据处理。Coze Loop 适用于企业自动化…

[GESP202309 四级] 2023年9月GESP C++四级上机题题解,附带讲解视频!

本文为2023年9月GESP C四级的上机题目的详细题解&#xff01;觉得写的不错或者有帮助可以点个赞啦。 目录 题目一讲解视频: 题目二讲解视频: 题目一:进制转换 解题思路: 代码(C): 题目二:变长编码 解题思路: 代码(C): 题目一讲解视频: 2023年9月GESP C四级上机题一题目…

【AI编程工具IDE/CLI/插件专栏】-国外IDE与Cursor能力对比

AI编程专栏(二) - Cursor 深度使用指南 Cursor 深度使用指南(二) - 新能力使用教程 从Trae 2.0与CodeBuddy IDE发布&#xff0c;谈大厂布局IDE 如何选择AI IDE&#xff1f;对比Cursor分析功能差异 AI编程工具IDE/CLI/插件专栏-热门AI编程CLI初识与IDE对 前面文章介绍过了国…

word2vector细致分解(CBOW, SKIP_GRAM, 层次soft Max, 负采样)

1 前世今生&#xff1a;NGRAM NGRAM&#xff1a;将词当成一个离散的单元&#xff08;因此存在一定的局限性&#xff0c;没有考虑到词与词之间的关系&#xff09; neural network language model&#xff1a;只能处理定长序列&#xff0c;训练慢。使用RNN之后有所改善 2 两种训…

Elasticsearch向量库

在Elasticsearch&#xff08;ES&#xff09;最新版本&#xff08;目前8.x系列&#xff09;中&#xff0c;无需额外的“embedding插件”&#xff0c;因为ES从7.14版本开始就原生支持向量数据类型&#xff08;dense_vector&#xff09; 和向量搜索能力&#xff0c;可直接作为向量…

嵌入式学习的第四十四天-ARM

一、ARM内核基础知识1.ALU算术逻辑单元&#xff1b;完成运算的电路2.通用寄存器&#xff1a;R0~R15R13&#xff08;SP&#xff09;&#xff1a;栈指针寄存器&#xff1a;指向栈的指针&#xff08;指向正确的位置&#xff09;&#xff0c;为了保护现场 R14&#xff08;LR…

QML开发:QML中的基本元素

文章目录一、概述二、常用基本元素2.1 基础视觉元素&#xff08;常用于布局和显示&#xff09;2.1.1 元素 Item 的介绍和使用2.1.2 元素 Rectangle 的介绍和使用2.1.3 元素 Image 的介绍和使用2.1.4 元素 Text 的介绍和使用2.2 交互元素&#xff08;用于接收用户操作&#xff0…

Spring AI 项目实战(二十二):Spring Boot + AI +DeepSeek实现智能合同数据问答助手​(附完整源码)

系列文章 序号 文章名称 1 Spring AI 项目实战(一):Spring AI 核心模块入门 2 Spring AI 项目实战(二):Spring Boot + AI + DeepSeek 深度实战(附完整源码) 3 Spring AI 项目实战(三):Spring Boot + AI + DeepSeek 打造智能客服系统(附完整源码) 4

从 0 到 1 创建 InfluxDB 3 表:标签、字段、命名规范一篇讲透

前言 在使用 InfluxDB 3 存储时序数据时,表的设计堪比盖房子打地基,地基打歪,数据“塌方”指日可待。InfluxDB 虽然不是传统意义上的关系型数据库,但它有自己的一套“审美”:标签(Tags)和字段(Fields)是它的双核心,谁先谁后,关系重大,顺序写错,查询性能立马打折。…

[sqlserver] 分析SQL Server中执行效率较低的SQL语句

查询性能分析较低的SQL语句 -- 查询性能分析 SELECT TOP 50qs.creation_time AS [编译时间],qs.last_execution_time AS [最后执行时间],qs.execution_count AS [执行次数],qs.total_worker_time/1000 AS [CPU总时间(ms)],qs.total_elapsed_time/1000 AS [总耗时(ms)],(qs.tota…