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

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 (最近最少使用算法), 中…...

计算机网络八股整理(一)

计算机网络八股文整理 一&#xff1a;网络模型 1&#xff1a;网络osi模型和tcp/ip模型分别介绍一下 osi模型是国际标准的网络模型&#xff0c;它由七层组成&#xff0c;从上到下分别是&#xff1a;应用层&#xff0c;表示层&#xff0c;会话层&#xff0c;传输层&#xff0c;…...

了解 CSS position 属性

CSS position 属性 在前端开发中&#xff0c;布局是一个至关重要的部分&#xff0c;而 CSS 的 position 属性是控制元素在页面中位置的核心工具。 本文将解释 CSS 中的 position 属性&#xff0c;包括其不同的值、效果及典型使用场景&#xff0c;以帮助你更好地理解和应用这一…...

数据结构 【二叉树(上)】

谈到二叉树&#xff0c;先来谈谈树的概念。 1、树的概念及结构 树是一种非线性的数据结构&#xff0c;它的逻辑关系看起来像是一棵倒着的树&#xff0c;也就是说它是根在上&#xff0c;而叶子在下的&#xff0c; 在树这种数据结构中&#xff0c;最顶端的结点称为根结点。在树的…...

C++11(中)

C11&#xff08;中&#xff09; 1.可变参数模板1.1.使用场景 2.lambda表达式&#xff08;重要&#xff09;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 &#x1f31f;&#x…...

下拉选择器,选择框,支持单选、多选、筛选和清空功能,支持vue2和vue3

下拉选择器&#xff0c;选择框&#xff0c;支持单选、多选、筛选和清空功能&#xff0c;支持vue2和vue3https://ext.dcloud.net.cn/plugin?id8159 点击即可。 注意数据来源&#xff1a; 选择的&#xff1a;valueName&#xff1a;选择下拉选择显示的显示屏...

HTTP中GET和POST的区别是什么?

HTTP定义&#xff1a; GET&#xff1a;用于获取资源&#xff0c;通常用于请求数据而不改变服务器的状态 POST&#xff1a;用于提交数据到服务器&#xff0c;通常会改变服务器的状态或产生副作用&#xff08;如创建或更新资源&#xff09; 参数传递方式&#xff1a; GET&…...

day04 企业级Linux安装及远程连接知识实践

1. 使用传统的网卡命名方式 在启动虚拟机时&#xff0c;按tab键进入编辑模式 添加命令&#xff1a; net.ifnames0 biosdevname0 这样linux系统会使用传统的网卡命名&#xff0c;例如eth0、eth1…… 2. 快照 做系统关键操作时&#xff0c;一定要使用快照(先将系统关机) 3.…...

jvm核心组件介绍

1. 类加载器&#xff08;ClassLoader&#xff09;&#xff1a; • 想象它是一个快递员&#xff0c;负责把Java类&#xff08;.class文件&#xff09;这个“包裹”从磁盘这个“发货地”送到JVM内部这个“目的地”。类加载器确保每个类只被加载一次&#xff0c;并维护一个类的层级…...

uname -m(machine) 命令用于显示当前系统的机器硬件架构(Unix Name)

文章目录 关于 arm64 架构检查是否安装了 Rosetta 2其他相关信息解释&#xff1a;命令功能&#xff1a;示例&#xff1a; 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->>设施类别阀门井 查询结果如下&#xff1a; 2.更新json字段中的某个属性值 UPDATE gis_coord_data SET attribute_data(attribute_data::jsonb ||{&quo…...

类的加载机制

类加载的概念 类加载是 Java 虚拟机&#xff08;JVM&#xff09;把字节码文件&#xff08;.class 文件&#xff09;转变为 Java 类型的复杂且关键的过程。这就如同把一份详细的设计图纸&#xff08;字节码文件&#xff09;加工成一个可以实际运行和使用的软件模块&#xff08;J…...

基于vite创建的react18项目的单元测试

题外话 最近一个小伙伴进了字节外包&#xff0c;第一个活就是让他写一个单元测试。 嗯&#xff0c;说实话&#xff0c;在今天之前我只知道一些理论&#xff0c;但是并没有实操过&#xff0c;于是我就试验了一下。 通过查询资料&#xff0c;大拿们基本都说基于vite的项目&…...

fiddler抓包工具与requests库构建自动化报告

一. Fiddler 抓包工具 1.1 Fiddler 工具介绍和安装 Fiddler 是一款功能强大的 HTTP 调试代理工具&#xff0c;能够全面记录并深入检查您的计算机与互联网之间的 HTTP 和 HTTPS 通信数据。其主界面布局清晰&#xff0c;主要包含菜单栏、工具栏、树形标签栏和内容栏。 1.2 Fid…...

Docker login 报证书存储错误的解决办法

文章目录 docker login 出现错误&#xff0c;提示&#xff1a;Error saving credentials: error storing credentials - err: exit status 1, out: Cannot autolaunch D-Bus without X11 $DISPLAY 环境 使用的是 Mint Linux &#xff0c;容器为 docker-ce 最新版 1 2 3 4 $…...

【自动化Selenium】Python 网页自动化测试脚本(上)

目录 1、Selenium介绍 2、Selenium环境安装 3、创建浏览器、设置、打开 4、打开网页、关闭网页、浏览器 5、浏览器最大化、最小化 6、浏览器的打开位置、尺寸 7、浏览器截图、网页刷新 8、元素定位 9、元素交互操作 10、元素定位 &#xff08;1&#xff09;ID定位 &…...

什么是MyBatis?

MyBatis简介 MyBatis是一款优秀的持久层框架&#xff0c;用于简化Java应用程序对数据库的操作。它曾是Apache的一个开源项目&#xff0c;名为iBatis&#xff0c;2010年迁移到Google Code并改名为MyBatis&#xff0c;2013年11月又迁移到了GitHub。 一、MyBatis的作用 在JavaE…...

TortoiseGit 将本地已有仓库推送到远程

TortoiseGit 将本地已有仓库推送到远程 一、创建线上仓库二、创建本地仓库三、提交内容到本地仓库四、添加远程仓库地址补充 一、创建线上仓库 在gitlab管理面页面按这前讲过的步骤创建一个空仓库。&#xff08;通常我们把服务器上这个仓库叫远程仓库&#xff0c;把我们自己电…...

腾讯云OCR车牌识别实践:从图片上传到车牌识别

在当今智能化和自动化的浪潮中&#xff0c;车牌识别&#xff08;LPR&#xff09;技术已经广泛应用于交通管理、智能停车、自动收费等多个场景。腾讯云OCR车牌识别服务凭借其高效、精准的识别能力&#xff0c;为开发者提供了强大的技术支持。本文将介绍如何利用腾讯云OCR车牌识别…...

TailwindCss 总结

目录 一、简介 二、盒子模型相关 三、将样式类写到一个类里面apply 四、一款TailWind CSS的UI库 一、简介 官方文档&#xff1a;Width - TailwindCSS中文文档 | TailwindCSS中文网 Tailwind CSS 的工作原理是扫描所有 HTML 文件、JavaScript 组件以及任何 模板中的 CSS 类…...

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...

Flask RESTful 示例

目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题&#xff1a; 下面创建一个简单的Flask RESTful API示例。首先&#xff0c;我们需要创建环境&#xff0c;安装必要的依赖&#xff0c;然后…...

【JavaEE】-- HTTP

1. HTTP是什么&#xff1f; HTTP&#xff08;全称为"超文本传输协议"&#xff09;是一种应用非常广泛的应用层协议&#xff0c;HTTP是基于TCP协议的一种应用层协议。 应用层协议&#xff1a;是计算机网络协议栈中最高层的协议&#xff0c;它定义了运行在不同主机上…...

23-Oracle 23 ai 区块链表(Blockchain Table)

小伙伴有没有在金融强合规的领域中遇见&#xff0c;必须要保持数据不可变&#xff0c;管理员都无法修改和留痕的要求。比如医疗的电子病历中&#xff0c;影像检查检验结果不可篡改行的&#xff0c;药品追溯过程中数据只可插入无法删除的特性需求&#xff1b;登录日志、修改日志…...

关于nvm与node.js

1 安装nvm 安装过程中手动修改 nvm的安装路径&#xff0c; 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解&#xff0c;但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后&#xff0c;通常在该文件中会出现以下配置&…...

最新SpringBoot+SpringCloud+Nacos微服务框架分享

文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的&#xff0c;根据Excel列的需求预估的工时直接打骨折&#xff0c;不要问我为什么&#xff0c;主要…...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建

华为云FlexusDeepSeek征文&#xff5c;DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色&#xff0c;华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型&#xff0c;能助力我们轻松驾驭 DeepSeek-V3/R1&#xff0c;本文中将分享如何…...

Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理

引言 Bitmap&#xff08;位图&#xff09;是Android应用内存占用的“头号杀手”。一张1080P&#xff08;1920x1080&#xff09;的图片以ARGB_8888格式加载时&#xff0c;内存占用高达8MB&#xff08;192010804字节&#xff09;。据统计&#xff0c;超过60%的应用OOM崩溃与Bitm…...

Reasoning over Uncertain Text by Generative Large Language Models

https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...

如何更改默认 Crontab 编辑器 ?

在 Linux 领域中&#xff0c;crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用&#xff0c;用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益&#xff0c;允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...