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

【高频面试题】LRU缓存

文章目录

    • 1 相关前置知识(OS)
    • 2 面试题 16.25. LRU 缓存
      • 2.1 题面
      • 2.2 示例
      • 2.3 解法1 (双端队列+哈希表)
        • 思路
      • 2.4 解法2
        • 思路
    • 3 参考

1 相关前置知识(OS)

  • 为什么需要页面置换算法:当进程运行时,若其访问的页面不在内存需要将其调入,但内存已无空闲空间时,就需要从内存中调出一页,换出到外存,选择调出哪个页面的算法就称为页面置换算法。页面的换入,换出需要磁盘I/O开销较大,因此要设计一个好的页面置换算法追求更低的缺页率。
  • 常见的页面置换算法:OPT(最佳置换算法) FIFO(先进先出置换算法) LRU(最近最久未使用置换算法) CLOCK(时钟置换算法)
  • 什么是LRU算法:简单来讲就是将一些元素放在一个容量固定的容器中进行存取,由于容器的容量有限,该容器就要保证那些最近才被用到的元素始终在容器内,而将已经很久没有用的元素剔除,实现容器内元素的动态维护。这种算法是一种缓存维护策略,因为缓存空间有限,让缓存中存储的都是最近才被用到的元素可以实现系统缓存的高效运作。

2 面试题 16.25. LRU 缓存

2.1 题面

设计和构建一个“最近最少使用”缓存,该缓存会删除最近最少使用的项目。缓存应该从键映射到值(允许你插入和检索特定键对应的值),并在初始化时指定最大容量。当缓存被填满时,它应该删除最近最少使用的项目。
它应该支持以下操作:

  • 获取数据 get 和 写入数据 put
  • 获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
  • 写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。
  • 注意:要求getput的时间复杂度为O(1)

2.2 示例

LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // 返回  1
cache.put(3, 3);    // 该操作会使得密钥 2 作废
cache.get(2);       // 返回 -1 (未找到)
cache.put(4, 4);    // 该操作会使得密钥 1 作废
cache.get(1);       // 返回 -1 (未找到)
cache.get(3);       // 返回  3
cache.get(4);       // 返回  4

2.3 解法1 (双端队列+哈希表)

思路
  • 使用双端队列维护,队尾是最近使用的,队头是最久未使用的,哈希表则维护key-value,此外注意在get时若元素存在则要重新将该元素移动到队列尾部,put若元素已存在,则要更新value,并将元素调整到队尾。
class LRUCache {
private:int capacity;std::deque<int> dq; std::unordered_map<int, int> mp;  
public:LRUCache(int capacity) : capacity(capacity) {}int get(int key) { // 若元素存在则将该元素移到队尾if (mp.find(key) != mp.end()) {dq.erase(std::remove(dq.begin(), dq.end(), key), dq.end());dq.push_back(key);return mp[key];}return -1;}void put(int key, int value) {if (mp.find(key) != mp.end()) {// 如果 key 已存在,更新 value,并调整到队尾mp[key] = value;dq.erase(std::remove(dq.begin(), dq.end(), key), dq.end());dq.push_back(key);} else {if (dq.size() == capacity) {int lru_key = dq.front();mp.erase(lru_key);dq.pop_front();}mp[key] = value;dq.push_back(key);}}
};

2.4 解法2

解法1的效率十分低,而且没有满足时间复杂度O(1)的条件,效率只击败了5%,我们想想要如何实现O(1),什么数据结构支持O(1)维护两端元素呢,我们可以用双向链表来实现。

思路
  • 使用双向链表+哈希表维护,表头维护最近使用的元素,表尾维护最久未使用的元素。这里我们要使用双向链表实现在表头添加一个元素、删除某个节点这两个操作。

  • 对于 get 操作,首先判断 key是否存在:如果 key 不存在,则返回 -1;如果 key 存在,则 key 对应的节点是最近被使用的节点。通过哈希表定位到该节点在双向链表中的位置,并将其移动到双向链表的头部,最后返回该节点的值。

  • 对于 put 操作,首先判断 key 是否存在:如果 key 不存在,使用 keyvalue 创建一个新的节点,在双向链表的头部添加该节点,并将 key 和该节点添加进哈希表中。然后判断双向链表的节点数是否超出容量,如果超出容量,则删除双向链表的尾部节点,并删除哈希表中对应的项;如果 key 存在,则与 get 操作类似,先通过哈希表定位,再将对应的节点的值更新为 value,并将该节点移到双向链表的头部。

这里引用灵神的题解,大家可以理解的更清晰。

class Node {
public:int key, value;Node *prev, *next;// 构造函数Node() : key(0), value(0), prev(nullptr), next(nullptr) {}Node(int _key, int _value) : key(_key), value(_value), prev(nullptr), next(nullptr) {}
};class LRUCache {
private:std::unordered_map<int, Node*> cache;Node* dummy;    // 头节点 表头维护最近使用的 表尾维护最久未使用的int capacity;// 删除一个节点void remove(Node *p) {p->prev->next = p->next;p->next->prev = p->prev;}// 表头添加一个节点void push_front(Node *p) {p->prev = dummy;p->next = dummy->next;p->prev->next = p;p->next->prev = p;}Node* get_node(int key) {auto it = cache.find(key);if (it == cache.end()) return nullptr;auto node = it->second;remove(node); // 将元素移出push_front(node); // 放在表头return node;}
public:LRUCache(int capacity) {this->capacity = capacity;dummy = new Node();dummy->next = dummy;dummy->prev = dummy;}int get(int key) {auto node = get_node(key);return node ? node->value : -1;}void put(int key, int value) {auto node = get_node(key);if (node) { //若存在该元素node->value = value; // 更新valuereturn ;}cache[key] = node = new Node(key, value);push_front(node); // 放在表头if (cache.size() > capacity) { // 超容量auto back = dummy->prev;cache.erase(back->key);remove(back); //去掉尾部元素delete back; // 释放内存}}
};/*** Your LRUCache object will be instantiated and called as such:* LRUCache* obj = new LRUCache(capacity);* int param_1 = obj->get(key);* obj->put(key,value);*/

3 参考

  • 【图解】一张图秒懂 LRU!
  • 面试题 16.25. LRU 缓存(C++【OPT/FIFO/LRU】操作系统页面置换算法一网打尽)

相关文章:

【高频面试题】LRU缓存

文章目录 1 相关前置知识&#xff08;OS&#xff09;2 面试题 16.25. LRU 缓存2.1 题面2.2 示例2.3 解法1 &#xff08;双端队列哈希表&#xff09;思路 2.4 解法2思路 3 参考 1 相关前置知识&#xff08;OS&#xff09; 为什么需要页面置换算法&#xff1a;当进程运行时&…...

讯联云库项目开发日志(二)AOP参数拦截

目录 利用AOP实现参数拦截: 一、​​HTTP请求进入Controller​&#xff08;发送邮件验证码&#xff09; 二、AOP切面触发 1. 切面拦截&#xff08;GlobalOperactionAspect.class&#xff09; method.getAnnotation()​​ null interceptor 判断​​ 2.参数校验注解 3. 参…...

龙虎榜——20250515

上证指数缩量收阴线&#xff0c;个股跌多涨少&#xff0c;上涨波段4月9日以来已有24个交易日&#xff0c;时间周期上处于上涨末端&#xff0c;注意风险。 深证指数缩量收阴线&#xff0c;日线上涨结束的概率在增大&#xff0c;注意风险。 2025年5月15日龙虎榜行业方向分析 一…...

知识图谱重构电商搜索:下一代AI搜索引擎的底层逻辑

1. 搜索引擎的进化论 从雅虎目录式搜索到Google的PageRank算法&#xff0c;搜索引擎经历了三次技术跃迁。而AI搜索引擎正在掀起第四次革命&#xff1a;在电商场景中&#xff0c;传统的「关键词匹配」已无法满足个性化购物需求&#xff0c;MOE搜索等新一代架构开始融合知识图谱…...

python-修改图片背景色

在Python中&#xff0c;可以使用图像处理库&#xff08;如OpenCV或Pillow&#xff09;来修改图片的背景色。通常&#xff0c;修改背景色的流程包括以下步骤&#xff1a; 1、对图片进行分割&#xff0c;识别前景和背景。 2、对背景区域进行颜色替换。 下面是两种实现方法&#x…...

卡洛诗,将高端西餐的冗余价值转化为普惠体验

西餐市场正经历一场结构性变革&#xff0c;一二线城市的高端西餐陷入内卷&#xff0c;而下沉市场却因品质与价格断层陷入选择困境——消费者既不愿为高价西餐的面子溢价买单&#xff0c;又难以忍受快餐式西餐的粗糙体验。这一矛盾催生了万亿级的市场真空地带&#xff0c;萨莉亚…...

【ROS2】ROS节点启动崩溃:rclcpp::exceptions::RCLInvalidArgument

1、问题描述 启动ROS节点时,直接崩溃,打印信息如下: terminate called after throwing an instance of rclcpp::exceptions::RCLInvalidArgumentwhat(): failed to create guard condition: context argument is null, at ./src/rcl/guard_condition.c:65 [ros2run]: Abo…...

Flutter在键盘的上方加一个完成按钮

有些情况下&#xff0c;输入框在输入键盘弹出后&#xff0c; 需要在键盘的上方显示一个toolbar &#xff0c; 然后 toolbar 上面一个完成按钮&#xff0c;点完成按钮把键盘关闭。 如图&#xff1a; 直接上代码&#xff0c;这样写的好处是&#xff0c;把 TextField 给封装了&…...

SQL注入---05--跨站注入

1 权限说明 select * from mysql.user; 这里的Y表示我前面的命令权限为root&#xff0c;n表示不支持root权限 导致结果&#xff1a; 如果为root的话&#xff0c;我就可操作这些命令并且可以进行跨数据库攻击&#xff0c;但是如果不是高权限root就无法执行这些操作 2 root权限…...

Vue 学习随笔系列二十三 -- el-date-picker 组件

el-date-picker 组件 文章目录 el-date-picker 组件el-date-picker 只有某些日期可选 el-date-picker 只有某些日期可选 <template><div><el-form ref"form" size"mini":model"form" :rules"rules"label-width"8…...

【免费分享】虚拟机VM(适用于 Windows)17.6.3

—————【下 载 地 址】——————— 【​本章下载一】&#xff1a;https://drive.uc.cn/s/7c4da5cd2af64 【​本章下载二】&#xff1a;https://pan.xunlei.com/s/VOQDkRRKc5OUVTauZezaiDEHA1?pwdpybg# 【百款黑科技】&#xff1a;https://ucnygalh6wle.feishu.cn/wiki/…...

Oracle中的select1条、几条、指定范围的语句

在Oracle中&#xff0c;可以使用不同的方法来选择一条记录、多条记录或指定范围内的记录。以下是具体的实现方式&#xff1a; 1. 查询单条记录 使用ROWNUM伪列限制结果为1条&#xff1a; SELECT * FROM your_table WHERE ROWNUM 1;特点&#xff1a;Oracle会在结果集生成时分…...

2025 后端自学UNIAPP【项目实战:旅游项目】5、个人中心页面:微信登录,同意授权,获取用户信息

一、框架以及准备工作 1、前端项目文件结构展示 2、后端项目文件结构展示 3、登录微信公众平台&#xff0c;注册一个个人的程序&#xff0c;获取大appid&#xff08;前端后端都需要&#xff09;和密钥&#xff08;后端需要&#xff09; 微信公众平台微信公众平台&…...

校园网规划与设计方案

一、项目概述 校园网是学校实现信息化教学、科研与管理的重要基础设施,其性能与稳定性直接影响学校的整体发展。随着学校规模不断扩大、教学科研活动日益丰富,对校园网的带宽、可靠性、安全性以及智能化管理等方面提出了更高要求。本规划与设计方案旨在构建一个高速、稳定、…...

蓝桥杯算法题 -蛇形矩阵(方向向量)

&#x1f381;个人主页&#xff1a;工藤新一 &#x1f50d;系列专栏&#xff1a;C面向对象&#xff08;类和对象篇&#xff09; &#x1f31f;心中的天空之城&#xff0c;终会照亮我前方的路 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 文章目录 P…...

配置VScodePython环境Python was not found;

Python was not found; run without arguments to install from the Microsoft Store, or disable this shortcut from Settings > Manage App Execution Aliases. 候试试重启电脑。 在卸载重装python后会出现难以解决的局面&#xff0c;系统变量&#xff0c;命令行&#…...

ollama 重命名模型

ollama 重命名模型 ollama list# 查看列表 ollama list # 生成原模型的Modelfile文件 ollama show --modelfile qwen3:32b > Modelfile # 从Modelfile文件创建新的模型 ollama create qwen3 -f Modelfile # 删除原模型 ollama rm qwen3:32b...

Qt信号槽机制与UI设计完全指南:从基础原理到实战应用

目录 前言一、信号槽1.1 传参1.2 Qt信号与槽的对应关系1.2.1一对多关系1.2.2 多对一关系 二、Designer三、Layout 布局3.1 基础用法3.2 打破布局3.3 贴合窗口3.4 伸展器&#xff08;Spacer&#xff09;3.5 嵌套布局 四、ui指针五、QWidget六、QLabel 标签使用指南总结 前言 本…...

Anaconda环境中conda与pip命令的区别

文章目录 conda与pip的基本区别在Anaconda环境中的实际差异安装包环境管理依赖解决示例最佳实践建议 常见问题解答 conda与pip的基本区别 包来源与生态系统 conda&#xff1a;从Anaconda默认仓库或conda-forge等渠道获取包 不仅管理Python包&#xff0c;还能管理非Python依赖&…...

XBL6501/02/03在POE设备上的应用方案

前言&#xff1a; 在当今数字化时代&#xff0c;POE&#xff08;Power over Ethernet&#xff09;设备因其能够通过以太网线同时传输数据和电力而被广泛应用。为了满足这些设备日益增长的电源需求&#xff0c;芯伯乐推出了XBL6501/02/03系列DC-DC电源芯片&#xff0c;为POE设备…...

编程题 03-树2 List Leaves【PAT】

文章目录 题目输入格式输出格式输入样例输出样例 题解解题思路完整代码 编程练习题目集目录 题目 Given a tree, you are supposed to list all the leaves in the order of top down, and left to right. 输入格式 Each input file contains one test case. For each case, …...

生信小白学Rust-03

语句和表达式 举个栗子&#x1f330; fn add_with_extra(x: i32, y: i32) -> i32 {let x x 1; // 语句let y y 5; // 语句x y // 表达式 } // 语句执行操作 // 表达式会返回一个值 怎么区分呢&#xff0c;目前我的理解是只要返回了值&#xff0c;那它就是表达式 fn…...

缺乏需求优先级划分时,如何合理分配资源?

当需求优先级不明确时&#xff0c;合理分配资源的关键在于建立统一评估标准、实施敏捷资源管理、提升团队协作效率、加强跨部门沟通机制。尤其是建立统一评估标准至关重要&#xff0c;它能帮助组织快速判断各项需求的重要性与紧迫性&#xff0c;从而实现资源的动态匹配与有效利…...

操作系统学习笔记第3章 内存管理(灰灰题库)

1. 单选题 某页式存储管理系统中&#xff0c;主存为 128KB&#xff0c;分成 32 块&#xff0c;块号为 0、1、2、3、…、31。某作业有 5 块&#xff0c;其页号为 0、1、2、3、4&#xff0c;被分别装入主存的 3、8、4、6、9 块中。有一逻辑地址为 [3, 70]&#xff08;其中方括号中…...

详细分析python 中的deque 以及和list 的用法区别

dqque :双端队列&#xff0c;可以快速的从另外一侧追加和推出对象,deque是一个双向链表&#xff0c;针对list连续的数据结构插入和删除进行优化。它提供了两端都可以操作的序列&#xff0c;这表示在序列的前后你都可以执行添加或删除操作。 通过上图可以看出&#xff0c;deque …...

Stack overflow

本文来源 &#xff1a;腾讯元宝 Stack Overflow - Where Developers Learn, Share, & Build Careers 开发者学习&#xff0c;分享 通过学习、工作和经验积累等方式&#xff0c;逐步建立和发展自己的职业生涯。 Find answers to your technical questions and help othe…...

和为target问题汇总

文章目录 习题377.组合总和 IV494.目标和 和为target的问题&#xff0c;可以有很多种问题的形式的考察&#xff0c;当然&#xff0c;及时的总结与回顾有利于我们熟练掌握这些知识&#xff01; 习题 377.组合总和 IV 377.组合总和 IV 思路分析&#xff1a;通过观察&#xff0…...

STM32单片机内存分配详细讲解

单片机的内存无非就两种&#xff0c;内部FLASH和SRAM&#xff0c;最多再加上一个外部的FLASH拓展。在这里我以STM32F103C8T6为例子讲解FLASH和SRAM。 STM32F103C8T6具有64KB的闪存和20KB的SRAM。 一. Flash 1.1 定义 非易失性存储器&#xff0c;即使在断电后&#xff0c;其所…...

RedHat7 如何更换yum镜像源

RedHat7如何更换yum镜像源&#xff1f; # 删除系统自带 yum rpm -qa|grep -e yum -e python-urlgrabber |xargs rpm -e --nodeps# 下载yum与wget的rpm软件包 curl -O http://mirrors.aliyun.com/centos/7/os/x86_64/Packages/yum-3.4.3-168.el7.centos.noarch.rpm curl -O ht…...

Ubuntu 编译SRS和ZLMediaKit用于视频推拉流

SRS实现视频的rtmp webrtc推流 ZLMediaKit编译生成MediaServer实现rtsp推流 SRS指定某个固定网卡&#xff0c;修改程序后重新编译 打开SRS-4.0.0/trunk/src/app/srs_app_rtc_server.cpp&#xff0c;在 232 行后面添加&#xff1a; ZLMediaKit编译后文件存放在ZLMediakit/rele…...