leetcode - LRU缓存
什么是 LRU
LRU (最近最少使用算法),
最早是在操作系统中接触到的, 它是一种内存数据淘汰策略, 常用于缓存系统的淘汰策略.
LRU算法基于局部性原理, 即最近被访问的数据在未来被访问的概率更高, 因此应该保留最近被访问的数据.
最近最少使用的解释
LRU (最近最少使用算法), 中的 "最近" 不是其绝对值的修饰, 而是一个范围.
如: 你最近去了那些地方, 最近看了哪些书.
而不是: 离你最近的人是谁, 离你最近的座位是哪一个.
了解了最近的意义, 那么串联起来就是: 最近使用的一堆数据中, 哪一个数据使用的是最少的
LRU原理
下面展示了 LRU 算法的基本原理.

可以看到, 在 LRU 算法中, 涉及到了对象的移动, 如果使用 数组 来作为缓存, 那么移动对象的效率很慢. 因为在这个算法中, 经常涉及到头插元素, 数组 的头插是O(n^2), 非常的慢.
所以推荐使用 双向链表 来实现.
146. LRU 缓存 - 力扣(LeetCode)
但是在题目中, 要求查找和插入的时间复杂度为O(1);
双向链表的插入删除时间复杂度为O(1), 但是查找的时间复杂度为O(n).
双向链表 + 哈希表
单使用双向链表, 查找的时间复杂度为O(n), 那么数据结构的查找操作的时间复杂度为O(1)?
答案很明显: 哈希表

定义链表节点 ListNode
struct ListNode
{
public:ListNode(){}ListNode(int k, int v):key(k),value(v){}~ListNode(){}int key;int value;// 节点中不仅存储 value, 还存储 key, 这在后面的 put 函数中有用ListNode* next;ListNode* prev;
};
LRUcache 成员属性
class LRUCache {
public:int _size = 0; // 记录缓存中已经缓存了多少数据int _capacity = 0; // 记录缓存大小 (可缓存的数据个数)ListNode* head = nullptr; // 双向链表的头节点ListNode* tail = nullptr; // 双向链表的尾节点unordered_map<int, ListNode*> table;// 底层是通过 hashtable 实现的map, 用来通过 kev 查找节点
}
LRUcache 成员方法
构造 / get / put 函数
class LRUCache {
public:LRUCache(int capacity) {_capacity = capacity; // 记录缓存的大小// 初始化链表的 头节点 和 尾节点head = new ListNode;tail = new ListNode;// 将头尾节点连接起来head->next = tail;head->prev = tail;tail->next = head;tail->prev = head;}// 通过 key 获取对应的 value. 如果 key 不存在, 则返回 -1int get(int key) {auto it = table.find(key); // 通过 hashtable 查找 key 是否存在if(it == table.end()){return -1; // 不存在对应的 [key, value], 返回 -1}// 存在 key, 记录value, 然后更新这个节点, 将这个节点移动到链表头部int ret = it->second->value;MoveToHead(it->second); // 将这个节点移动到头部return ret;}// 插入一对键值对 [key, value]void put(int key, int value) {auto it = table.find(key); // 在 hashtable 中查找是否已经存在 keyif(it != table.end()) // 已经存在 key 则更新节点的值, 并且将这个节点移动到链表头部{// 更新节点it->second->value = value;MoveToHead(it->second); // 将节点移动到链表头部return; // 直接返回, 下面是进行插入的操作}// key 不存在, 判断 空间是否已满, 满了就需要删除 链表末尾的节点if(_size == _capacity){// ListNode 中记录的 key 就起作用了, 如果只有 value, 那么就还需要遍历 tableint back = tail->prev->key;table.erase(back); // 删除 hashtable 中这个节点的记录pop_back(); // 删除尾部节点--_size;}// 链表末尾的节点已被删除, 现在需要向 链表头部 插入 新的节点ListNode* node = push_front(key, value);table[key] = node; // 在 hashtable 中记录这个新的节点++_size;}
};
MoveToHead / push_front / pop_back 函数
class LRUCache {
public:// 将 node 移动到链表头部void MoveToHead(ListNode* node){if(node == head->next) // 如果这个节点就是头部, 那么就不移动{return;}ListNode* node_next = node->next; // 记录 node 节点的后一个节点ListNode* node_prev = node->prev; // 记录 node 节点的前一个节点node_prev->next = node_next; // 将 node 的前后节点连接起来node_next->prev = node_prev;// 将 node 节点链接到链表首部node->prev = head; node->next = head->next;head->next->prev = node;head->next = node;}// 头插ListNode* push_front(int key, int value){ListNode* node = new ListNode(key, value);ListNode* next = head->next;head->next = node;node->prev = head;next->prev = node;node->next = next;return node;}// 尾删void pop_back(){ListNode* prev = tail->prev->prev;ListNode* cur = tail->prev;prev->next = tail;tail->prev = prev;delete cur;}
};

完整代码
class LRUCache {
public:struct ListNode{public:ListNode(){}ListNode(int k, int v):key(k),value(v){}~ListNode(){}int key;int value;ListNode* next;ListNode* prev;};int _size = 0;int _capacity = 0;ListNode* head = nullptr;ListNode* tail = nullptr;unordered_map<int, ListNode*> table;LRUCache(int capacity) {_capacity = capacity;head = new ListNode;tail = new ListNode;head->next = tail;head->prev = tail;tail->next = head;tail->prev = head;}int get(int key) {auto it = table.find(key);if(it == table.end()){return -1;}int ret = it->second->value;MoveToHead(it->second); // 将这个节点移动到头部return ret;}void put(int key, int value) {auto it = table.find(key);if(it != table.end()){// 更新节点it->second->value = value;MoveToHead(it->second);return;}if(_size == _capacity){int back = tail->prev->key;table.erase(back); // 删除 hashtable 中的键值对pop_back(); // 删除尾部节点--_size;}ListNode* node = push_front(key, value);table[key] = node;++_size;}void MoveToHead(ListNode* node){if(node == head->next){return;}ListNode* node_next = node->next;ListNode* node_prev = node->prev;node_prev->next = node_next;node_next->prev = node_prev;node->prev = head;node->next = head->next;head->next->prev = node;head->next = node;}ListNode* push_front(int key, int value){ListNode* node = new ListNode(key, value);ListNode* next = head->next;head->next = node;node->prev = head;next->prev = node;node->next = next;return node;}void pop_back(){ListNode* prev = tail->prev->prev;ListNode* cur = tail->prev;prev->next = tail;tail->prev = prev;delete cur;}};
相关文章:
leetcode - LRU缓存
什么是 LRU LRU (最近最少使用算法), 最早是在操作系统中接触到的, 它是一种内存数据淘汰策略, 常用于缓存系统的淘汰策略. LRU算法基于局部性原理, 即最近被访问的数据在未来被访问的概率更高, 因此应该保留最近被访问的数据. 最近最少使用的解释 LRU (最近最少使用算法), 中…...
计算机网络八股整理(一)
计算机网络八股文整理 一:网络模型 1:网络osi模型和tcp/ip模型分别介绍一下 osi模型是国际标准的网络模型,它由七层组成,从上到下分别是:应用层,表示层,会话层,传输层,…...
了解 CSS position 属性
CSS position 属性 在前端开发中,布局是一个至关重要的部分,而 CSS 的 position 属性是控制元素在页面中位置的核心工具。 本文将解释 CSS 中的 position 属性,包括其不同的值、效果及典型使用场景,以帮助你更好地理解和应用这一…...
数据结构 【二叉树(上)】
谈到二叉树,先来谈谈树的概念。 1、树的概念及结构 树是一种非线性的数据结构,它的逻辑关系看起来像是一棵倒着的树,也就是说它是根在上,而叶子在下的, 在树这种数据结构中,最顶端的结点称为根结点。在树的…...
C++11(中)
C11(中) 1.可变参数模板1.1.使用场景 2.lambda表达式(重要)2.1.使用说明2.2.函数对象与lambda表达式 3.线程库3.1.thread3.2.atomic原子库操作3.3.mutex3.3.1.mutex的种类3.3.2.lock_guard3.3.3.unique_lock 🌟&#x…...
下拉选择器,选择框,支持单选、多选、筛选和清空功能,支持vue2和vue3
下拉选择器,选择框,支持单选、多选、筛选和清空功能,支持vue2和vue3https://ext.dcloud.net.cn/plugin?id8159 点击即可。 注意数据来源: 选择的:valueName:选择下拉选择显示的显示屏...
HTTP中GET和POST的区别是什么?
HTTP定义: GET:用于获取资源,通常用于请求数据而不改变服务器的状态 POST:用于提交数据到服务器,通常会改变服务器的状态或产生副作用(如创建或更新资源) 参数传递方式: GET&…...
day04 企业级Linux安装及远程连接知识实践
1. 使用传统的网卡命名方式 在启动虚拟机时,按tab键进入编辑模式 添加命令: net.ifnames0 biosdevname0 这样linux系统会使用传统的网卡命名,例如eth0、eth1…… 2. 快照 做系统关键操作时,一定要使用快照(先将系统关机) 3.…...
jvm核心组件介绍
1. 类加载器(ClassLoader): • 想象它是一个快递员,负责把Java类(.class文件)这个“包裹”从磁盘这个“发货地”送到JVM内部这个“目的地”。类加载器确保每个类只被加载一次,并维护一个类的层级…...
uname -m(machine) 命令用于显示当前系统的机器硬件架构(Unix Name)
文章目录 关于 arm64 架构检查是否安装了 Rosetta 2其他相关信息解释:命令功能:示例: dgqdgqdeMac-mini / % uname -m arm64您运行的 uname -m 命令显示您的系统架构是 arm64。这意味着您的 Mac Mini 使用的是 Apple 的 M1 或更新的芯片&…...
Pgsql:json字段查询与更新
1.查询json字段的值 SELECT attribute_data->>设施类别 mycol, * FROM gis_coord_data WHERE attribute_data->>设施类别阀门井 查询结果如下: 2.更新json字段中的某个属性值 UPDATE gis_coord_data SET attribute_data(attribute_data::jsonb ||{&quo…...
类的加载机制
类加载的概念 类加载是 Java 虚拟机(JVM)把字节码文件(.class 文件)转变为 Java 类型的复杂且关键的过程。这就如同把一份详细的设计图纸(字节码文件)加工成一个可以实际运行和使用的软件模块(J…...
基于vite创建的react18项目的单元测试
题外话 最近一个小伙伴进了字节外包,第一个活就是让他写一个单元测试。 嗯,说实话,在今天之前我只知道一些理论,但是并没有实操过,于是我就试验了一下。 通过查询资料,大拿们基本都说基于vite的项目&…...
fiddler抓包工具与requests库构建自动化报告
一. Fiddler 抓包工具 1.1 Fiddler 工具介绍和安装 Fiddler 是一款功能强大的 HTTP 调试代理工具,能够全面记录并深入检查您的计算机与互联网之间的 HTTP 和 HTTPS 通信数据。其主界面布局清晰,主要包含菜单栏、工具栏、树形标签栏和内容栏。 1.2 Fid…...
Docker login 报证书存储错误的解决办法
文章目录 docker login 出现错误,提示:Error saving credentials: error storing credentials - err: exit status 1, out: Cannot autolaunch D-Bus without X11 $DISPLAY 环境 使用的是 Mint Linux ,容器为 docker-ce 最新版 1 2 3 4 $…...
【自动化Selenium】Python 网页自动化测试脚本(上)
目录 1、Selenium介绍 2、Selenium环境安装 3、创建浏览器、设置、打开 4、打开网页、关闭网页、浏览器 5、浏览器最大化、最小化 6、浏览器的打开位置、尺寸 7、浏览器截图、网页刷新 8、元素定位 9、元素交互操作 10、元素定位 (1)ID定位 &…...
什么是MyBatis?
MyBatis简介 MyBatis是一款优秀的持久层框架,用于简化Java应用程序对数据库的操作。它曾是Apache的一个开源项目,名为iBatis,2010年迁移到Google Code并改名为MyBatis,2013年11月又迁移到了GitHub。 一、MyBatis的作用 在JavaE…...
TortoiseGit 将本地已有仓库推送到远程
TortoiseGit 将本地已有仓库推送到远程 一、创建线上仓库二、创建本地仓库三、提交内容到本地仓库四、添加远程仓库地址补充 一、创建线上仓库 在gitlab管理面页面按这前讲过的步骤创建一个空仓库。(通常我们把服务器上这个仓库叫远程仓库,把我们自己电…...
腾讯云OCR车牌识别实践:从图片上传到车牌识别
在当今智能化和自动化的浪潮中,车牌识别(LPR)技术已经广泛应用于交通管理、智能停车、自动收费等多个场景。腾讯云OCR车牌识别服务凭借其高效、精准的识别能力,为开发者提供了强大的技术支持。本文将介绍如何利用腾讯云OCR车牌识别…...
TailwindCss 总结
目录 一、简介 二、盒子模型相关 三、将样式类写到一个类里面apply 四、一款TailWind CSS的UI库 一、简介 官方文档:Width - TailwindCSS中文文档 | TailwindCSS中文网 Tailwind CSS 的工作原理是扫描所有 HTML 文件、JavaScript 组件以及任何 模板中的 CSS 类…...
边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...
大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
JDK 17 新特性
#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的ÿ…...
成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...
ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...
【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...
Redis:现代应用开发的高效内存数据存储利器
一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发,其初衷是为了满足他自己的一个项目需求,即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源,Redis凭借其简单易用、…...
【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看
文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...
阿里云Ubuntu 22.04 64位搭建Flask流程(亲测)
cd /home 进入home盘 安装虚拟环境: 1、安装virtualenv pip install virtualenv 2.创建新的虚拟环境: virtualenv myenv 3、激活虚拟环境(激活环境可以在当前环境下安装包) source myenv/bin/activate 此时,终端…...
