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

【图解版】力扣第146题:LRU缓存

力扣第146题:LRU缓存

  • 一、LRU算法
    • 1. 基本概念
    • 2. LRU 和 LFU 的区别:
    • 3. 为什么 LRU 不需要记录使用频率?
  • 二、Golang代码实现
  • 三、代码图解
    • 1. LRUCache、DLinkedNode两个结构体
    • 2. 初始化结构体对象
    • 3. addToHead函数
    • 4. removeNode函数
    • 5. moveToHead函数
    • 6. removeTail函数
    • 7. Get函数
    • 8. Put函数

一、LRU算法

1. 基本概念

在 LRU 算法中,首部节点的含义是最近最常访问的节点,而不是使用频率最高的节点。LRU(Least Recently Used) 是一种基于最近使用时间而非使用频率的缓存淘汰算法,核心思想是:最近使用的数据应该优先保留,最近很久未使用的数据应该被淘汰。

2. LRU 和 LFU 的区别:

  • LRU(Least Recently Used):基于数据的使用时间,最近访问的节点会移动到链表头部,而最久未访问的节点会被淘汰。它只关注最后一次访问的时间,不记录具体的访问次数。
  • LFU(Least Frequently Used):基于数据的使用频率,频率最高的节点会优先保留,频率最低的节点会被淘汰。

3. 为什么 LRU 不需要记录使用频率?

在 LRU 算法中,只需要维护每个节点的访问顺序,而不需要记录节点的访问次数。每次访问某个节点时,将该节点移动到链表的头部,而最久未使用的节点则自然在链表尾部。所以要获取最近访问的节点,直接访问链表的头部节点即可。

二、Golang代码实现

type LRUCache struct {size intcapacity intcache map[int]*DLinkedNodehead, tail *DLinkedNode
}type DLinkedNode struct {key, val intprev, next *DLinkedNode
}func initDLinkedNode(key, val int) *DLinkedNode {return &DLinkedNode{key: key,val: val,}
}func Constructor(capacity int) LRUCache {l := LRUCache{cache: map[int]*DLinkedNode{},head: initDLinkedNode(0, 0),tail: initDLinkedNode(0, 0),capacity: capacity,}l.head.next = l.taill.tail.prev = l.headreturn l
}func (this *LRUCache) addToHead(node *DLinkedNode) {node.prev = this.headnode.next = this.head.nextthis.head.next.prev = nodethis.head.next = node
}func (this *LRUCache) removeNode(node *DLinkedNode) {// 将节点从链表中单独抽出来node.prev.next = node.nextnode.next.prev = node.prev
}func (this *LRUCache) moveToHead(node *DLinkedNode) {this.removeNode(node)this.addToHead(node)
}func (this *LRUCache) removeTail() *DLinkedNode {node := this.tail.prevthis.removeNode(node)delete(this.cache, node.key)return node
}// 通过cache的map关系,找到对应的值,该值存储在node的val属性中。
func (this *LRUCache) Get(key int) int {// 如果key是不存在的,那就返回-1if _, ok := this.cache[key]; !ok {return -1}node := this.cache[key]this.moveToHead(node)return node.val
}func (this *LRUCache) Put(key int, val int)  {if _, ok := this.cache[key]; !ok {node := initDLinkedNode(key, val)this.cache[key] = nodethis.addToHead(node)this.size++if this.size > this.capacity {removed := this.removeTail()delete(this.cache, removed.key)this.size--}} else {node := this.cache[key]node.val = valthis.moveToHead(node)}
}

三、代码图解

1. LRUCache、DLinkedNode两个结构体

type LRUCache struct {size intcapacity intcache map[int]*DLinkedNodehead, tail *DLinkedNode
}type DLinkedNode struct {key, val intprev, next *DLinkedNode
}

在这里插入图片描述

map理解为一个存储键值对映射的地方,来(1,1),就存储(1,1),来(2,2),就存储(2,2)。

至于这些(key,val)的顺序,就用链表来控制。为了方便插入、删除节点,所以采用双向链表。

将map和双向链表结合起来,就是将map的val值设置为DoubleNode类型(双向链表类型),DoubleNode里面设置有key,val两个属性(不是映射哦),这里的key和map的key是一样大小的值。

最后的效果就是:通过map的key找到DoubleNode节点,然后找到该节点里面的val属性,至于(key,val)的顺序,是由双向链表去排的,map就是个映射到节点的地方,找到节点,就等于找到val。

2. 初始化结构体对象

func initDLinkedNode(key, value int) *DLinkedNode {return &DLinkedNode{key: key,value: value,}
}func Constructor(capacity int) LRUCache {l := LRUCache{cache: map[int]*DLinkedNode{},head: initDLinkedNode(0, 0),tail: initDLinkedNode(0, 0),capacity: capacity,}l.head.next = l.taill.tail.prev = l.headreturn l
}

在这里插入图片描述

3. addToHead函数

func (this *LRUCache) addToHead(node *DLinkedNode) {node.prev = this.headnode.next = this.head.nextthis.head.next.prev = nodethis.head.next = node
}

请添加图片描述

注意:这里关于节点的顺序,其实是在结构体外去排的,这个节点的顺序并不是排在两个结构体内的哦。

4. removeNode函数

// 将节点从链表中单独抽出来
func (this *LRUCache) removeNode(node *DLinkedNode) {node.prev.next = node.nextnode.next.prev = node.prev
}

请添加图片描述

5. moveToHead函数

func (this *LRUCache) moveToHead(node *DLinkedNode) {this.removeNode(node)this.addToHead(node)
}

把node节点从链表中打断,抽出来,然后将node节点移到this.head后面

6. removeTail函数

func (this *LRUCache) removeTail() *DLinkedNode {node := this.tail.prevthis.removeNode(node)delete(this.cache, node.key)return node
}

请添加图片描述
因为map的key无法直接获得,而node.key和map的key一样,所以用node.key。

7. Get函数

// 通过cache的map关系,找到对应的值,该值存储在node的val属性中。
func (this *LRUCache) Get(key int) int {// 如果key是不存在的,那就返回-1if _, ok := this.cache[key]; !ok {return -1}node := this.cache[key]this.moveToHead(node)return node.val
}

8. Put函数

func (this *LRUCache) Put(key int, val int)  {if _, ok := this.cache[key]; !ok {node := initDLinkedNode(key, val)this.cache[key] = nodethis.addToHead(node)this.size++if this.size > this.capacity {removed := this.removeTail()delete(this.cache, removed.key)this.size--}} else {node := this.cache[key]node.val = valthis.moveToHead(node)}
}

!ok是表示如果map里面的key为空,那就创建一个相应的新节点,让map存储一下key和该节点的映射关系,然后将该节点插入到链表头部,

如果超过LRUCahce的容量,就删除最后一个节点。

如果map里面的key不是空的,那就更新一下map存储的节点,然后将其移动到头部。

相关文章:

【图解版】力扣第146题:LRU缓存

力扣第146题:LRU缓存 一、LRU算法1. 基本概念2. LRU 和 LFU 的区别:3. 为什么 LRU 不需要记录使用频率? 二、Golang代码实现三、代码图解1. LRUCache、DLinkedNode两个结构体2. 初始化结构体对象3. addToHead函数4. removeNode函数5. moveToH…...

数据库知识点整理

DDL DDL-数据库操作 show databases ------------ 查看所有数据库 select database(); ----------查看当前数据库 create database 数据库名;---- 创建数据库 use 数据库名; --------------使用数据库 drop database 数据库名;--…...

【JVM】内存模型

文章目录 内存模型的基本概念案例 程序计数器栈Java虚拟机栈局部变量表栈帧中局部变量表的实际状态栈帧中存放的数据有哪些 操作数栈帧数据 本地方法栈 堆堆空间是如何进行管理的? 方法区静态变量存储 直接内存直接内存的作用 内存模型的基本概念 在前面的学习中,我们知道了字…...

代码随想录:二叉树的四种遍历

144. 二叉树的前序遍历 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), right(nullpt…...

【Linux】从多线程同步到生产者消费者模型:多线程编程实践

目录 1.线程的同步 1.1.为什么需要线程的同步? 2.2.条件变量的接口函数 2.生产消费模型 2.1 什么是生产消费模型 2.2.生产者消费者模型优点 2.3.为何要使用生产者消费者模型 3.基于BlockingQueue的生产者消费者模型 3.1为什么要将if判断变成while&#xff…...

如何在word里面给文字加拼音?

如何在word里面给文字加拼音?在现代社会,阅读已经成为了我们日常生活中不可或缺的一部分。尤其是在学习汉语的过程中,拼音的帮助显得尤为重要。为了帮助大家更好地理解和掌握汉字的发音,许多教师和学生都希望能够在Word文档中为文…...

Detr论文精读

摘要: 作者提到,该方法将物体检测看做直接的集合预测,在传统的目标检测算法中,会先生成候选区域,然后对每个候选区域进行单独的预测(包括物体的分类和预测框的回归),集合预测就是直…...

找寻孤独伤感视频素材的热门资源网站推荐

在抖音上,伤感视频总是能够引起观众的共鸣,很多朋友都在寻找可以下载伤感视频素材的地方。作为一名资深的视频剪辑师,今天我来分享几个提供高清无水印伤感素材的网站,如果你也在苦苦寻找这些素材,不妨看看以下推荐&…...

大模型~合集13

我自己的原文哦~ https://blog.51cto.com/whaosoft/12302606 #TextRCNN、TextCNN、RNN 小小搬运工周末也要学习一下~~虽然和世界没关 但还是地铁上看书吧, 大老勿怪 今天来说一下 文本分类必备经典模型 模型 SOTA!模型资源站收录情况 模型来源论文 RAE ​​ht…...

【Next.js 项目实战系列】04-修改 Issue

原文链接 CSDN 的排版/样式可能有问题,去我的博客查看原文系列吧,觉得有用的话,给我的库点个star,关注一下吧 上一篇【Next.js 项目实战系列】03-查看 Issue 修改 Issue 添加修改 Button​ 本节代码链接 安装 Radix UI 的 Ra…...

【Linux】并行与并发(含时间片)

简单来说 并发:多个进程轮流使用同一个CPU,在逻辑层面上,一段时间内推进完成了多个进程 并行:机器中有多个CPU可以使用,在物理层面上,做到同一时间会有多个进程同时在运行 举个例子:一群人需要…...

【Flutter】页面布局:弹性布局(Flex)

在 Flutter 开发中,布局是非常重要的部分。布局系统允许开发者控制和管理界面上的组件如何排列和展示。弹性布局(Flex)是其中一个非常强大且常用的布局组件,它能够在水平方向或垂直方向上灵活调整子组件的空间分配比例。Row 和 Co…...

深入解析 Go 语言接口:多接口实现与接口组合的实际应用

文章目录 一、引言二、一个类型实现多个接口1. 定义多个接口2. 类型实现多个接口3. 使用多个接口 三、接口的组合1. 接口嵌套2. 实现复合接口 四、实际开发中的应用场景1. 多态与模块化设计2. 松耦合系统设计3. 测试与依赖注入4. 事件驱动架构中的应用 五、小结 一、引言 在 G…...

Eclipse——Java开发详解

Eclipse 1、配置JDK2、设置编译版本2.1、全局编译版本2.2、项目编译版本2.3、Web项目编译版本 3、设置工作目录4、创建Java项目5、配置Tomcat6、创建Web项目7、配置Maven8、创建Maven项目8.1、普通Maven项目8.2、Maven Web项目 9、创建SpringBoot项目10、设置字体11、设置代码提…...

练手小项目推荐

以下是一些练手项目推荐,我可以给你一些适合学生毕业设计的小项目建议,既可以锻炼技能,也能完成学术要求。以下是一些可行的毕业设计项目建议: 校园导航APP 功能:为校园内的新生和访客提供导航,标记教室、…...

一图秒懂色彩空间和色彩模型

色彩空间和色彩模型 想必学过图像处理或者摄影的小伙伴都知道这两个词,看了一些博客,发现很少有人把这两个概念说清楚的,大多数都是混在一起,色彩模型和色彩空间的概念混为一谈,很让人疑惑。   这里我们用一张图来解…...

控制Stable Diffusion生成质量的多种方法

在Stable Diffusion绘图中,控制AI生成图像的质量可以通过多种方法来实现。以下是几种常见的方法: 1. 从底模控制(Checkpoint) 使用不同的模型检查点(Checkpoints)可以显著影响生成图像的质量和细节。选择一…...

递归算法笔记

根据b站视频整理的 **视频地址:**https://www.bilibili.com/video/BV1S24y1p7iH/?spm_id_from333.788.videopod.sections&vd_source6335ddc7b30e1f4510569db5f2506f20 最常见的一个递归例子: 斐波那契数列:1,2,3…...

Android——发送彩信

跳转到相册选择图片 btn_jump.setOnClickListener(new View.OnClickListener() {Overridepublic void onClick(View view) {// 跳转到系统相册选择图片并返回Intent intent new Intent(Intent.ACTION_GET_CONTENT);// 设置图片类型为图片类型intent.setType("image/*&quo…...

对比迁移项目的改动

文章目录 对比迁移项目的改动场景背景解决方案 对比迁移项目的改动 场景背景 同源定制化项目,同一套代码扩展出来的项目(从领导口中得知) A项目的有三维地图展示,项目B跑起来却加载不出来,但是本地运行A项目代码&…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道(多模态 OCR → 语义检索 → 答案渲染)、两级检索(倒排 BM25 向量 HNSW)并以大语言模型兜底”的整体框架: 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后,分别用…...

工程地质软件市场:发展现状、趋势与策略建议

一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...

剑指offer20_链表中环的入口节点

链表中环的入口节点 给定一个链表,若其中包含环,则输出环的入口节点。 若其中不包含环,则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...

OkHttp 中实现断点续传 demo

在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...

【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具

第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...

全志A40i android7.1 调试信息打印串口由uart0改为uart3

一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...

Spring AI与Spring Modulith核心技术解析

Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...

蓝桥杯3498 01串的熵

问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798&#xff0c; 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码

目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&#x1f…...