leetcode146.LRU缓存,从算法题引入,全面学习LRU和链表哈希表知识

leetcode146. LRU 缓存

题目链接
请你设计并实现一个满足 LRU (最近最少使用) 缓存约束的数据结构。

实现 LRUCache 类:
LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存

int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。

void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。

函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。
示例:
输入
[“LRUCache”, “put”, “put”, “get”, “put”, “get”, “put”, “get”, “get”, “get”]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出
[null, null, null, 1, null, -1, null, -1, 3, 4]

解释

LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1);    // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2);    // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1);    // 返回 -1 (未找到)
lRUCache.get(3);    // 返回 3
lRUCache.get(4);    // 返回 4

LRU是什么?

最近最少使用算法(LRU)是大部分操作系统为最大化页面命中率而广泛采用的一种页面置换算法。具体解释见百度百科
该算法的思路是,发生缺页中断时,选择未使用时间最长的页面置换出去。
利用 LRU 算法对例子进行页面置换的结果如图所示。当进程第一次对页面 2 进行访问时,由于页面 7 是最近最久未被访问的,故将它置换出去。之后对页面0进行了访问,导致页面1比页面0成为了最久未使用的页。所以之后当进程第一次对页面 3进行访问时,第 1 页成为最近最久未使用的页,将它换出。
在这里插入图片描述

LRU实现

在实现上,LRU通常使用一个哈希表和一个双向链表来实现。哈希表存储键和链表节点的指针,而双向链表则按照数据被访问的顺序来组织数据。当缓存满了之后,链表尾部的节点(即最近最少使用的节点)会被移除,以腾出空间给新加入的数据。

1.定义结构体

首先,定义了一个名为Dlinkednode的结构体,它代表双向链表中的一个节点。每个节点包含以下成员:

int key//键值。
int value//与键值关联的数据。
Dlinkednode* pre//指向前一个节点的指针。
Dlinkednode* nex//指向后一个节点的指针。
构造函数:Dlinkednode()是默认构造函数,初始化所有成员为默认值。
Dlinkednode(int _key, int _value)是带参数的构造函数,用于创建具有特定键值和数据的节点。

2.定义LRU缓存类

接下来,定义了一个名为LRUCache的类,它用于实现LRU缓存机制。类中包含以下成员:

Dlinkednode* head//指向双向链表头部的指针。
Dlinkednode* tail//指向双向链表尾部的指针。
unordered_map<int, Dlinkednode*> cache//哈希表,用于存储键值和对应节点的指针。
int size//当前缓存中元素的数量。
int capacity//缓存的最大容量。
LRUCache(int _capacity)是类的构造函数,它初始化缓存的最大容量,
并创建一个空的双向链表,链表的头尾节点不存储实际数据,仅用于维护链表结构。
int get(int key)//获取键值对应的数据。如果键值不存在,则返回-1。
//如果键值存在,则将其对应的节点移动到链表头部(最近使用),并返回节点的数据。
void put(int _key, int _value)//添加或更新键值对。如果键值不存在,则创建新节点并添加到链表头部。
//如果键值已存在,则更新其数据并移动节点到链表头部。
//如果添加新节点后缓存超出容量,则移除链表尾部的节点(最少使用),并从哈希表中删除对应的键值。
void movetohead(Dlinkednode* node)//将节点移动到链表头部,表示该节点最近被使用。
void removenode(Dlinkednode* node)//从链表中移除指定节点。
void addtohead(Dlinkednode* node)//将节点添加到链表头部。
Dlinkednode* removetail()//移除并返回链表尾部的节点,表示该节点是最少使用的。

3.函数具体实现

get函数首先检查哈希表中是否存在键值,如果不存在,则返回-1。如果存在,则获取对应的节点指针,并调用movetohead函数将其移动到链表头部,最后返回节点的值。

put函数首先检查哈希表中是否存在键值。如果不存在,创建新节点并将其添加到链表头部,然后更新哈希表。如果键值已存在,则更新节点的值并移动到链表头部。如果缓存已满,则调用removetail函数移除最少使用的节点,并更新哈希表。

movetohead、removenode、addtohead和removetail函数是辅助函数,用于维护双向链表的结构,确保节点可以正确地添加到头部或从尾部移除。

具体代码如下:

// 定义双向链表节点结构体
struct Dlinkednode {int key;           // 节点的键值int value;         // 节点的值Dlinkednode* pre;  // 指向前一个节点的指针Dlinkednode* nex;  // 指向后一个节点的指针// 构造函数,初始化节点的所有成员Dlinkednode() : key(0), value(0), pre(NULL), nex(NULL) {}Dlinkednode(int _key, int _value) : key(_key), value(_value), pre(NULL), nex(NULL) {}
};// 定义LRU缓存类
class LRUCache {
private:Dlinkednode* head;  // 双向链表的头部节点,不存储实际数据Dlinkednode* tail;  // 双向链表的尾部节点,不存储实际数据unordered_map<int, Dlinkednode*> cache;  // 哈希表,存储键和节点指针的映射int size;  // 当前缓存中的元素数量int capacity;  // 缓存的最大容量public:// 构造函数,初始化缓存的最大容量和双向链表的头尾节点LRUCache(int _capacity) : capacity(_capacity), size(0) {head = new Dlinkednode();tail = new Dlinkednode();head->nex = tail;  // 头部节点的下一个指向尾部节点tail->pre = head;  // 尾部节点的上一个指向头部节点}// 获取缓存中键值对应的数据int get(int key) {if (cache.count(key) == 0) {return -1;  // 如果键值不在缓存中,返回-1}Dlinkednode* node = cache[key];  // 获取节点指针movetohead(node);  // 将节点移动到链表头部,表示最近使用return node->value;  // 返回节点的值}// 添加或更新缓存中的键值对void put(int _key, int _value) {if (cache.count(_key) == 0) {Dlinkednode* node = new Dlinkednode(_key, _value);  // 创建新节点cache[_key] = node;  // 更新哈希表addtohead(node);  // 将新节点添加到链表头部++size;  // 缓存大小加1if (size > capacity) {Dlinkednode* removed = removetail();  // 移除链表尾部节点cache.erase(removed->key);  // 从哈希表中删除键值delete removed;  // 删除节点--size;  // 缓存大小减1}} else {Dlinkednode* node = cache[_key];  // 获取已存在的节点指针node->value = _value;  // 更新节点的值movetohead(node);  // 将节点移动到链表头部}}// 将节点移动到链表头部void movetohead(Dlinkednode* node) {removenode(node);  // 从当前位置移除节点addtohead(node);  // 将节点添加到链表头部}// 从链表中移除指定节点void removenode(Dlinkednode* node) {node->pre->nex = node->nex;  // 前一个节点的下一个指向当前节点的下一个node->nex->pre = node->pre;  // 后一个节点的上一个指向当前节点的上一个}// 将节点添加到链表头部void addtohead(Dlinkednode* node) {node->nex = head->nex;  // 新节点的下一个指向头节点的下一个node->pre = head;  // 新节点的上一个指向头节点head->nex->pre = node;  // 头节点的下一个的上一个指向新节点head->nex = node;  // 头节点的下一个指向新节点}// 移除并返回链表尾部的节点Dlinkednode* removetail() {Dlinkednode* node = tail->pre;  // 获取尾部节点的前一个节点removenode(node);  // 从链表中移除该节点return node;  // 返回被移除的节点}
};/*** 下面是LRUCache类的使用示例:* 创建一个容量为capacity的LRUCache对象* LRUCache* obj = new LRUCache(capacity);* 通过get(key)获取键为key的数据,如果不存在则返回-1* int param_1 = obj->get(key);* 通过put(key, value)添加或更新键为key的数据,值为value* obj->put(key, value);*/

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

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

相关文章

【QT】父子按钮同时响应点击事件

QPushButton如何响应点击事件 QPushButton::event(QEvent *e) 。可以看到在QPushButton中的event函数中并没有鼠标点击相关的操作&#xff0c;那么我们去QAbstractButton::event中寻找 damn it!。依然没有那我们去QWidget::event中寻找 damn it! 只有mousePressEvent mouseR…

libcef.dll丢失的解决方法-多种libcef.dll亲测有效解决方法分享

libcef.dll是Chromium Embedded Framework (CEF)的核心动态链接库&#xff0c;它为开发者提供了一个将Chromium浏览器嵌入到本地桌面应用程序中的解决方案。这个库使得开发者能够利用Chromium的强大功能&#xff0c;如HTML5、CSS3、JavaScript等&#xff0c;来创建跨平台的应用…

罕见!史诗级“大堵船”

新加坡港口的停泊延误时间已延长至7天&#xff0c;积压的集装箱数量达到惊人的450000标准箱&#xff0c;远超新冠疫情暴发时期的数轮高点。业内认为&#xff0c;近期东南亚恶劣的天气情况加剧了该区域港口拥堵。 5月31日&#xff0c;上海航运交易所&#xff08;下称“航交所”…

重生奇迹MU召唤师如何学习狂暴术?

一、了解狂暴术的基本信息 狂暴术是一种非常强大的技能&#xff0c;可以让召唤师的攻击力和防御力大幅度提高&#xff0c;但同时也会增加自身的伤害。在使用狂暴术之前&#xff0c;召唤师需要仔细考虑自己的状态和对手的情况。 二、学习狂暴术的方法 1.通过任务学习 在游戏…

Docker安装与使用 --学习笔记

一、概述 Docker是什么? Docker是一种工具&#xff0c;类似于一个虚拟箱子&#xff0c;可以把软件和它运行所需要的环境打包放进这个箱子里。这样&#xff0c;无论这个箱子放到哪里&#xff0c;软件都能像在原来的地方一样运行&#xff0c;不会因为换了地方就出问题。 假设…

【Java】一文看懂Thread 线程池的 7 种创建方式、任务队列及自定义线程池(代码示例)

本文摘要&#xff1a;【Java】Thread 线程池的 7 种创建方式及自定义线程池&#xff08;代码示例版&#xff09; &#x1f60e; 作者介绍&#xff1a;我是程序员洲洲&#xff0c;一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专…

彩灯控制器设计 74ls160+ne555实现

一、选题背景 数字电子技术在我们生活中的应用非常之广泛,不论是在各个方面都会涉及到它,小到家用电器的自动控制,大到神舟九号和天空一号航天器的设计,都无可避免的要运用它。并且鉴于以理论推动实践及理论实践相结合为指导思想,特此用我们所学的理论知识来实践这次课程设…

【云原生】Docker Compose 使用详解

目录 一、前言 二、Docker Compose 介绍 2.1 Docker Compose概述 2.2 Docker Compose特点 2.3 Docker Compose使用场景 三、Docker Compose 搭建 3.1 安装docker环境 3.2 Docker Compose安装方式一 3.2.1 下载最新版/如果不是最新可替换最新版本 3.2.2 设置权限 3.2.…

Q-Learning 简介:初学者教程(1)

一、说明 强化学习强调无模型学习算法&#xff0c;因此出现Q-Learning&#xff0c;Q-Learning算法酷似“有限状态自动机”模型&#xff0c;只是增加了奖励机制和Agent机制&#xff0c;而Agent与粒子群算法、蒙特卡洛算法是有关的。本文介绍这个算法框架。 &#xff0c; 二、QL框…

C语言—深入理解指针(5)

1. sizeof 和 strlen 的对比 1.1 sizeof 在学习操作符的时候&#xff0c;我们学习了 sizeof&#xff0c;sizeof 是计算变量所占内存空间大小的&#xff0c;单位是字节&#xff0c;如果操作数是类型的话&#xff0c;计算的是使用类型创建的变量所占内存空间的大小。 sizeof 只…

西瓜播放器xgplayer设置自动播放踩坑

上图是官网&#xff08;西瓜视频播放器官方中文文档&#xff09;的介绍&#xff0c;相信大家都是按照官网配置去做的&#xff0c;但是并没有什么用&#xff0c;插件很好用&#xff0c;但是属性不全&#xff0c;真的很悔恨&#xff0c;找遍 api 都没有找到自动播放的属性&#x…

MongoDB-4.2.1 之安装和使用

安装 下载安装包 我自己电脑是 Windows7 的老古董&#xff0c;所以就下载老版本的 MongoDB。 mongodb: https://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2012plus-4.2.1.zip 解压安装包到指定路径 我解压到的 C 盘 C:\mongodb-4.2.1 添加环境变量 创建数据库和…

【免费Web系列】JavaWeb实战项目案例六

这是Web第一天的课程大家可以传送过去学习 http://t.csdnimg.cn/K547r 员工信息-删除&修改 前面我们已经实现了员工信息的条件分页查询以及新增操作。 关于员工管理的功能&#xff0c;还有两个需要实现&#xff1a; 删除员工 修改员工 除了员工管理的功能之外&#x…

基于springboot+vue的家乡特色推荐系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

【Linux】Linux工具——gcc/g++

1.使用vim更改信用名单——sudo 我们这里来补充sudo的相关知识——添加信任白名单用户 使用sudo就必须将使用sudo的那个账号添加到信用名单里&#xff0c;而且啊&#xff0c;只有超级管理员才可以添加 信用名单在/etc/sudoers里 我们发现它的权限只是可读啊&#xff0c;所以…

分享:重庆耶非凡科技有限公司人力资源项目靠不靠谱?

在当今快速变化的商业环境中&#xff0c;人力资源项目作为企业发展的重要支撑&#xff0c;其专业性和可靠性成为企业选择合作伙伴时的重要考量因素。重庆耶非凡科技有限公司作为一家在行业内颇具影响力的科技企业&#xff0c;其人力资源项目——人力RPO(招聘流程外包)项目&…

dm8 什么时候视图中统计的内存会超过OS

v$bufferpool和v$mem_pool视图记录着DMSERVER各组件的内存占用量。理论上跟OS看到的保持一致。但实际大多数场景下&#xff0c;OS中看到的数据远大于视图中的统计。这里面可能有内存泄漏的原因。不过也有的时候视图中的统计数据超过OS。下面就是这种情况&#xff1a; 上图中红线…

Java_Mybatis

Mybatis是一款优秀的持久层框架&#xff0c;用户简化JDBC(使用Java语言操作关系型数据库的一套API)开发 使用Mybatis查询所有用户数据&#xff1a; 代码演示&#xff1a; UserMapper&#xff1a; Mapper //被调用时会通过动态代理自动创建实体类&#xff0c;并放入IOC容器中…

用cloudflared 把家里电脑网站可以让任何人试用

一针见血说技术&#xff0c;通俗易懂去实现自己的想法 一、背景 搭建一个网站&#xff0c;或者有个自己开发的算法&#xff0c;需要供应少量的人免费服务&#xff0c;也可以用于向合伙人演示。需要一个云服务&#xff0c;要么购买。还得啰嗦学习一些网站的开通知识&#xff0…

单号日入50+,全自动挂机赚钱

大家好&#xff01;今天我为大家精心挑选了一个极具潜力的副业项目——“游戏工作室自由之刃2&#xff1a;单号日入50&#xff0c;全自动挂机赚钱”。 传奇游戏&#xff0c;无疑是许多人心中那段青春时光的珍贵回忆。 即便是其手游版本&#xff0c;也依旧保持着极高的热度和人…