LRU缓存
什么是LRU缓存?
LRU(Least Recently Used)是最近最少使用算法,是操作系统中用于分页置换的算法,如果要向内存中添加分页,并且内存分页已满的情况下,就选出最近一段时间最不常用的分页进行置换(例如将最不常用的分页暂时放到磁盘,这时内存就有一个空闲分页,将新增分页放过来即可)。
题目
链接:leetcode.146. 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)
的平均时间复杂度运行。
分析
一、 容器内容的设计
我们可以用一个容器对数据进行缓存,第一个元素表示最近最不经常使用,最后一个元素表示最近最经常使用,从前往后依次过渡
二、 数据结构的选型
① 使用数组
假设将内容全部用数组进行缓存
现在使用元素2,那么应该将2移到尾部,3、4、5全部向前移动。这样查询的时间复杂度为O(1),移动的时间复杂度为O(n)。
那么题目要求的get时间复杂度就为O(n),put时间复杂度为O(n)。显然不能使用数组。
② 使用链表
如果使用元素2,那么需要找到元素2,然后将元素2放到尾部即可。这样查询的时间复杂度为O(n),移动的时间复杂度为O(1),
显然对于查询操作我们可以使用哈希去优化,使得查询的时间复杂度为O(1)
③ 哈希+ 链表
这样查询2只需要通过key映射,查询效率为O(1),将2移动到链表尾部,1指向3即可,移动效率为O(1),完美!
但是这里有个问题,我们通过key定位到元素2时,如何获取2前面的元素?
聪明的小伙伴已经想出来了,答案就是将单链表变为双向链表
④ 哈希 + 双向链表
这样不管是查询还是移动,复杂度都是O(1)
解答
① 设计双向链表节点结构
class ListNode {// 节点前驱ListNode pre;// 节点后继ListNode next;// 哈希中的keyint key;// 数据域,记录缓存内容int val;// 构造函数public ListNode() {}public ListNode(int key, int val) {this.key = key;this.val = val;}
}
这里节点中为什么要设计key呢?因为如果添加元素时缓存区已满,就需要删除链表中第一个元素(最近最不常使用的元素),同时还要在哈希中移除,所以需要记录这个数据的key
② 使用带假节点的双向链表
为什么使用假节点?原因很简单,操作方便。对于一个单链表,添加元素时要判断头指针是否为空,删除元素时需要判断是不是头部元素,如果是头部元素,就需要将头指针向后移,但是使用假节点之后便不再需要进行这些操作。结构如下:
可以看出,对于带有假节点的链表,head后邻节点是有效的第一个节点(在不是rear的情况下),rear前邻节点是最后一个有效节点(在不是head的情况下)
③ 初始化LRU容器
// 头部指针
private ListNode head;
// 尾部指针
private ListNode rear;
// 哈希表
private HashMap<Integer, ListNode> hash = new HashMap<>();
// 缓存容量
private final int capacity;public LRUCache(int capacity) {// 缓存容量this.capacity = capacity;// 初始化双向链表// 头部假节点this.head = new ListNode();// 尾部假节点this.rear = new ListNode();// 首尾相连head.next = rear;rear.pre = head;
}
目前为止,双向链表还是一个空链表
④ 获取缓存元素
思路如下:
-
元素不存在,直接返回-1
-
元素存在
-
将元素移到链表尾部
-
返回元素
-
代码实现:
public int get(int key) {// 从哈希表中查找节点ListNode node = hash.get(key);// 节点不存在,返回-1if (node == null) {return -1;}// 节点存在,将节点移动到链表尾部moveToLast(node);// 返回缓存数据return node.val;
}
⑤ 放置缓存元素
思路如下:
-
元素不存在
-
如果缓存已满,删除链表第一个元素
-
添加新节点到链表尾部
-
-
元素存在
-
将元素移动到链表尾部
-
更节点数据
-
代码实现:
public void put(int key, int value) {// 判断key是否存在ListNode valueNode = hash.get(key);// 元素不存在if (valueNode == null) {// 判断缓存是否到达上限if (hash.size() == capacity) {// 删除最不经常使用的节点ListNode firstNode = head.next;removeNode(firstNode);}// 添加新节点到链表尾部addNodeToBack(new ListNode(key, value));} else {// 节点存在,将节点移到尾部,并修改节点值moveToLast(valueNode);valueNode.val = value;}
}
⑥ 操作节点函数的封装
将节点放置到尾部
private void moveToLast(ListNode node) {// 如果节点在链表中,那么更新他的前驱与后继的指针指向if (node.pre != null && node.next != null) {ListNode pre = node.pre;ListNode next = node.next;pre.next = next;next.pre = pre;}// 将节点插入到尾部ListNode rearPre = rear.pre;rearPre.next = node;node.pre = rearPre;node.next = rear;rear.pre = node;
}
添加节点到尾部并放入哈希表
private void addNodeToBack(ListNode newNode) {// 将节点移动到尾部moveToLast(newNode);// 将节点映射信息放入哈希表hash.put(newNode.key, newNode);
}
删除节点
private void removeNode(ListNode node) {// 移除哈希表中存储的信息hash.remove(node.key);// 移除链表中的节点ListNode pre = node.pre;ListNode next = node.next;pre.next = next;next.pre = pre;
}
代码实现
class LRUCache {private static class ListNode {public ListNode pre;public ListNode next;public int key;public int val;public ListNode() {}public ListNode(int key, int val) {this.key = key;this.val = val;}}private ListNode head;private ListNode rear;private HashMap<Integer, ListNode> hash = new HashMap<>();private final int capacity;public LRUCache(int capacity) {this.capacity = capacity;// 初始化双向链表this.head = new ListNode();this.rear = new ListNode();head.next = rear;rear.pre = head;}public int get(int key) {// 从哈希表中查找节点ListNode node = hash.get(key);// 节点不存在,返回-1if (node == null) {return -1;}// 节点存在,将节点移动到链表尾部moveToLast(node);// 返回缓存数据return node.val;}public void put(int key, int value) {// 判断key是否存在ListNode valueNode = hash.get(key);// 元素不存在if (valueNode == null) {// 判断缓存是否到达上限if (hash.size() == capacity) {// 删除最不经常使用的节点ListNode firstNode = head.next;removeNode(firstNode);}// 添加新节点到链表尾部addNodeToBack(new ListNode(key, value));} else {// 节点存在,将节点移到尾部,并修改节点值moveToLast(valueNode);valueNode.val = value;}}private void addNodeToBack(ListNode newNode) {moveToLast(newNode);hash.put(newNode.key, newNode);}private void moveToLast(ListNode node) {// 如果节点在链表中,那么更新他的前驱与后继的指针指向if (node.pre != null && node.next != null) {ListNode pre = node.pre;ListNode next = node.next;pre.next = next;next.pre = pre;}// 将节点插入到尾部ListNode rearPre = rear.pre;rearPre.next = node;node.pre = rearPre;node.next = rear;rear.pre = node;}private void removeNode(ListNode node) {hash.remove(node.key);ListNode pre = node.pre;ListNode next = node.next;pre.next = next;next.pre = pre;}
}
相关文章:
LRU缓存
什么是LRU缓存? LRU(Least Recently Used)是最近最少使用算法,是操作系统中用于分页置换的算法,如果要向内存中添加分页,并且内存分页已满的情况下,就选出最近一段时间最不常用的分页进行置换(…...
.net6 使用 FreeSpire.XLS 实现 excel 转 pdf - docker 部署
FreeSpire.XLS && Aspose.Cells包都可以实现。实现过程中发现如下问题: 本地测试通过, docker部署服务器后报错: The type initializer for Spire.Xls.Core.Spreadsheet.XlsPageSetupBase threw an exception. 由于缺少依赖…...
HttpServletRequest req和前端的关系,req.getParameter详细解释,req.getParameter和前端的关系
HttpServletRequest 对象在后端和前端之间起到了桥梁的作用,它包含了来自客户端的所有请求信息。通过 HttpServletRequest 对象,后端可以获取前端发送的请求参数、请求头、请求方法等信息,并根据这些信息进行相应的处理。以下是对 HttpServle…...
[Python3] Sanic 框架构建高并发的 Web 服务
在 Python3 中使用 Sanic 框架来构建高并发的 Web 服务时,Sanic 因其异步和基于事件驱动的架构能够很好地处理高并发请求。下面是如何使用 Sanic 的一些要点和示例代码。 1. 安装 Sanic 首先确保你安装了 Sanic,可以通过以下命令安装: pip…...
5.5 W5500 TCP服务端与客户端
文章目录 1、TCP介绍2、W5500简介2.1 关键函数socketlistensendgetSn_RX_RSRrecv自动心跳包检测getSn_SR 1、TCP介绍 TCP 服务端: 创建套接字[socket]:服务器首先创建一个套接字,这是网络通信的端点。绑定套接字[bind]:服务器将…...
【Flutter】搭建Flutter开发环境,安卓开发
Flutter是谷歌开源的一个跨平台开发的框架,方便好用,这里以Windows 上构建 Flutter Android 应用为例,记录下我搭建环境时碰到的一些问题以及解决。 第一步:参考官网:开发 Android 应用 | Flutter 中文文档 - Flutter …...
【机器学习】——朴素贝叶斯模型
💻博主现有专栏: C51单片机(STC89C516),c语言,c,离散数学,算法设计与分析,数据结构,Python,Java基础,MySQL,linux…...
k8s rainbond centos7/win10 -20241124
参考 https://www.rainbond.com/ 国内一站式云原生平台 对centos7环境支持不太行 [lighthouseVM-16-5-centos ~]$ curl -o install.sh https://get.rainbond.com && bash ./install.sh 2024-11-24 09:56:57 ERROR: Ops! Docker daemon is not running. Start docke…...
ctfshow单身杯2024wp
文章目录 ctfshow单身杯2024wp签到好玩的PHPezzz_sstiez_inject ctfshow单身杯2024wp 签到好玩的PHP 考点:序列化反序列化 <?phperror_reporting(0);highlight_file(__FILE__);class ctfshow {private $d ;private $s ;private $b ;private $ctf ;public …...
深入解密 K 均值聚类:从理论基础到 Python 实践
1. 引言 在机器学习领域,聚类是一种无监督学习的技术,用于将数据集分组成若干个类别,使得同组数据之间具有更高的相似性。这种技术在各个领域都有广泛的应用,比如客户细分、图像压缩和市场分析等。聚类的目标是使得同类样本之间的…...
【代码pycharm】动手学深度学习v2-08 线性回归 + 基础优化算法
课程链接 线性回归的从零开始实现 import random import torch from d2l import torch as d2l# 人造数据集 def synthetic_data(w,b,num_examples):Xtorch.normal(0,1,(num_examples,len(w)))ytorch.matmul(X,w)bytorch.normal(0,0.01,y.shape) # 加入噪声return X,y.reshape…...
Python绘制太极八卦
文章目录 系列目录写在前面技术需求1. 图形绘制库的支持2. 图形绘制功能3. 参数化设计4. 绘制控制5. 数据处理6. 用户界面 完整代码代码分析1. rset() 函数2. offset() 函数3. taiji() 函数4. bagua() 函数5. 绘制过程6. 技术亮点 写在后面 系列目录 序号直达链接爱心系列1Pyth…...
sklearn学习
介绍:scaler:换算的意思 1. 归一化MinMaxScaler() 归一化的意思是将一堆数,如果比较离散,为了让数据更适合模型训练,将离散的数据压缩到0到1之间,以方便模型更高效优质的学习,而对数据的预处理…...
# [Unity] 【游戏开发】Unity开发基础2-Unity脚本编程基础详解
Unity脚本编程是创建互动式游戏体验的核心技能之一。本文将详细讲解Unity脚本编程的基础知识,包括变量和数据类型、程序逻辑、方法等方面,并通过实例展示如何使用这些基本知识完成简单功能的实现。 1. 新建Unity脚本的基本结构 当在Unity中创建一个脚本时,Unity会生成如下基…...
【强化学习的数学原理】第02课-贝尔曼公式-笔记
学习资料:bilibili 西湖大学赵世钰老师的【强化学习的数学原理】课程。链接:强化学习的数学原理 西湖大学 赵世钰 文章目录 一、为什么return重要?如何计算return?二、state value的定义三、Bellman公式的详细推导四、公式向量形式…...
C语言-数学基础问题
一.奇数、偶数问题 1.从键盘上输入一个整数,判断并输出它是奇数还是偶数。 //从键盘上输入一个整数,判断并输出它是奇数还是偶数。 main() {int i;printf("输入一个整数:\n");scanf("%d",&i);if(i%20)printf("它是偶数\n…...
2024算法基础公选课练习四(综合2)
一、前言 最后几个题确实有难度,这次有两题没整出来 二、题目总览 三、具体题目 3.1 问题 A: 水题系列1-B(班级排位) 思路 最暴力的思路是写线段树,然后暴力枚举两个端点,总体时间复杂度为O(n^2*logn)最坏会到1e9的数量级,可能…...
小程序-使用 iconfont 图标库报错:Failed to load font
官方默认可以忽略此错误,在清除缓存后首次刷新会显示此错误,重新渲染错误消失 解决方法: 在 iconfont 图标库选择项目设置 选中 Base64 保存,重新点击链接 -> 复制代码到项目中 操作步骤:...
鲸鱼机器人和乐高机器人的比较
鲸鱼机器人和乐高机器人各有其独特的优势和特点,家长在选择时可以根据孩子的年龄、兴趣、经济能力等因素进行综合考虑,选择最适合孩子的教育机器人产品。 优势 鲸鱼机器人 1)价格亲民:鲸鱼机器人的产品价格相对乐高更为亲民&…...
解决单元测试时找不到类名
场景: springboot单元测试mockito对mapper进行mock时: tk.mybatis.mapper.mapperexception: 无法获取实体类 XX.xx 对应的表名 分析: 使用了一个方法:Example examplenew Example(User.class); 进入源码后发现Entityhelper没…...
簡單易懂:如何在Windows系統中修改IP地址?
無論是為了連接到一個新的網路,還是為了解決網路連接問題,修改IP地址都是一個常見的操作。本文將詳細介紹如何在Windows系統中修改IP地址,包括靜態IP地址的設置和動態IP地址的獲取。 IP地址是什麼? IP地址是互聯網協議地址的簡稱…...
(详细文档!)java swing学生信息管理系统 +mysql
第一章:系统功能分析 1.1、系统简介与开发背景 学生信息管理系统是在信息化时代,特别是在教育领域中产生的。随着学校规模的不断扩大和信息化技术的不断发展,传统的纸质档案管理方式已经无法满足学校对学生信息管理的需求,因此需…...
OSG开发笔记(三十三):同时观察物体不同角度的多视图从相机技术
若该文为原创文章,未经允许不得转载 本文章博客地址:https://blog.csdn.net/qq21497936/article/details/143932273 各位读者,知识无穷而人力有穷,要么改需求,要么找专业人士,要么自己研究 长沙红胖子Qt…...
[极客大挑战 2019]BabySQL--详细解析
信息搜集 进入界面: 输入用户名为admin,密码随便输一个: 发现是GET传参,有username和password两个传参点。 我们测试一下password点位能不能注入: 单引号闭合报错,根据报错信息,我们可以判断…...
Java的字符串操作(二)(代码示例)
1. 字符串的定义 // 直接赋值方式定义字符串 String str1 "Hello World";// 使用new关键字定义字符串 String str2 new String("Hello World");// 可以通过打印对象的哈希码来查看是否是同一个对象(在一定程度上反映引用情况) Sy…...
2024-2025 ICPC, NERC, Southern and Volga Russian Regional Contest(ABCGJLN)
文章目录 N. Fixing the Expression思路code J. Waiting for...思路code C. DIY思路code L. Bridge Renovation思路code A. Bonus Project思路code G. Guess One Character思路code B. Make It Equal思路code N. Fixing the Expression 思路 签到题,只改变中间的字…...
SpringBoot(9)-Dubbo+Zookeeper
目录 一、了解分布式系统 二、RPC 三、Dubbo 四、SpringBootDubboZookeeper 4.1 框架搭建 4.2 实现RPC 一、了解分布式系统 分布式系统:由一组通过网络进行通信,为了完成共同的任务而协调工作的计算机节点组成的系统 二、RPC RPC:远程…...
现代密码学
概论 计算机安全的最核心三个关键目标(指标)/为:保密性 Confidentiality、完整性 Integrity、可用性 Availability ,三者称为 CIA三元组 数据保密性:确保隐私或是秘密信息不向非授权者泄漏,也不被非授权者使…...
websocket是什么?
一、定义 Websocket是一种在单个TCP连接上进行全双工通信的协议,它允许服务器主动向客户端推送数据,而不需要客户端不断的轮询服务器来获取数据 与http协议不同,http是一种无状态的,请求,响应模式的协议(单向通信)&a…...
idea怎么打开两个窗口,运行两个项目
今天在开发项目的时候,前端希望运行一下以前的项目,于是就需要开两个 idea 窗口,运行两个项目 这里记录一下如何设置:首先依次点击: File -> Settings -> Appearance & Behavior ->System Settings 看到如…...
aws服务--机密数据存储KMS(1)介绍和使用
在AWS(Amazon Web Services)中存储机密数据时,安全性和合规性是最重要的考虑因素。AWS 提供了多个服务和工具,帮助用户确保数据的安全性、机密性以及合规性。AWS Secrets Manager、KMS(Key Management Service)是推荐的存储机密数据的AWS服务和最佳实践。这里先看KMS。 …...
怎么编译OpenWrt镜像?-基于Widora开发板
1.准备相应的环境,我使用的环境是VMware16ubuntu20.04,如图1所示安装编译所需的依赖包; sudo apt-get install build-essential asciidoc binutils bzip2 gawk gettext git libncurses5-dev libz-dev patch python3 python2.7 unzip zlib1g-…...
Android opencv使用Core.hconcat 进行图像拼接
Android 集成OpenCV-CSDN博客 import org.opencv.android.Utils; import org.opencv.core.Core; import org.opencv.core.CvType; import org.opencv.core.Mat; import org.opencv.imgcodecs.Imgcodecs; import android.graphics.Bitmap; import android.graphics.BitmapFactor…...
小杨的N字矩阵c++
题目描述 小杨想要构造一个m*m 的 N 字矩阵( m为奇数),这个矩阵的从左上角到右下角的对角线、第1 列和第m 列都 是半角加号 ,其余都是半角减号 - 。例如,一个 5*5 的 N 字矩阵如下: --- -- -- -- --- 请…...
C 语言面向对象
面向对象的基本特性:封装,继承,多态 1.0 面向过程概念 当我们在编写程序时,通常采用以下步骤: 1. 将问题的解法分解成若干步骤 2. 使用函数分别实现这些步骤 3. 依次调用这些函数 这种编程风格的被称作 面向过程…...
初试无监督学习 - K均值聚类算法
文章目录 1. K均值聚类算法概述2. k均值聚类算法演示2.1 准备工作2.2 生成聚类用的样本数据集2.3 初始化KMeans模型对象,并指定类别数量2.4 用样本数据训练模型2.5 用训练好的模型生成预测结果2.6 输出预测结果2.7 可视化预测结果 3. 实战小结 1. K均值聚类算法概述…...
部署实战(二)--修改jar中的文件并重新打包成jar文件
一.jar文件 JAR 文件就是 Java Archive ( Java 档案文件),它是 Java 的一种文档格式JAR 文件与 ZIP 文件唯一的区别就是在 JAR 文件的内容中,多出了一个META-INF/MANIFEST.MF 文件META-INF/MANIFEST.MF 文件在生成 JAR 文件的时候…...
C++中的原子操作:原子性、内存顺序、性能优化与原子变量赋值
一、原子操作与原子性 原子操作(atomic operation)是并发编程中的一个核心概念,指的是在多线程环境中,一个操作一旦开始,就不会被其他线程的操作打断,直至该操作完成。这种不可分割的特性保证了操作的原子…...
【JavaEE初阶】多线程初阶下部
文章目录 前言一、volatile关键字volatile 能保证内存可见性 二、wait 和 notify2.1 wait()方法2.2 notify()方法2.3 notifyAll()方法2.4 wait 和 sleep 的对比(面试题) 三、多线程案例单例模式 四、总结-保证线程安全的思路五、对比线程和进程总结 前言…...
数据结构(Java版)第二期:包装类和泛型
目录 一、包装类 1.1. 基本类型和对应的包装类 1.2. 装箱和拆箱 1.3. 自动装箱和自动拆箱 二、泛型的概念 三、引出泛型 3.1. 语法规则 3.2. 泛型的优点 四、类型擦除 4.1. 擦除的机制 五、泛型的上界 5.1. 泛型的上界的定义 5.2. 语法规则 六、泛型方法 6.1…...
[原创](Modern C++)现在C++的关键性概念: 通俗易懂的解释“多态“与“虚函数“的内在关系
常用网名: 猪头三 出生日期: 1981.XX.XX 企鹅交流: 643439947 个人网站: 80x86汇编小站 编程生涯: 2001年~至今[共23年] 职业生涯: 21年 开发语言: C/C、80x86ASM、PHP、Perl、Objective-C、Object Pascal、C#、Python 开发工具: Visual Studio、Delphi、XCode、Eclipse、C Bui…...
python操作Elasticsearch
使用elasticsearch 6.x版本,操作es数据。 #! -*- coding:utf-8 -* import timefrom elasticsearch import Elasticsearch, helpersclass EstUtil:_instance Nonedef __new__(cls, *args, **kwargs):if not cls._instance:cls._instance super(EstUtil, cls).__ne…...
【数据分享】2024年我国省市县三级的住宿服务设施数量(8类住宿设施/Excel/Shp格式)
宾馆酒店、旅馆招待所等住宿服务设施的配置情况是一个城市公共基础设施完善程度的重要体现,一个城市住宿服务设施种类越丰富,数量越多,通常能表示这个城市的公共服务水平越高! 本次我们为大家带来的是我国各省份、各地级市、各区…...
从尾到头打印链表 剑指offer
题目描述 输入一个链表的头节点,从尾到头反过来打印出每个节点的值。 链表节点定义如下: struct ListNode {int m_nKey;ListNode*m_pNext; }; 代码实现 栈实现: 递归实现: 但是用递归实现可能存在的问题:...
【测试工具JMeter篇】JMeter性能测试入门级教程(二)出炉,测试君请各位收藏了!!!
上篇文章:CSDN 我们介绍了JMeter的一些原理介绍,以及安装配置和启动流程,本文我们就来讲讲JMeter如何使用。 一、JMeter目录结构组成 1. 根目录 Jmeter安装包解压后的根目录如下图: 1.1 backups目录:脚本备份目录&am…...
【JavaEE】Servlet:表白墙
文章目录 一、前端二、前置知识三、代码1、后端2、前端3、总结 四、存入数据库1、引入 mysql 的依赖,mysql 驱动包2、创建数据库数据表3、调整上述后端代码3.1 封装数据库操作,和数据库建立连接3.2 调整后端代码 一、前端 <!DOCTYPE html> <ht…...
WPF中如何让Textbox显示为一条直线
由于Textbox直接使用是一条直线 设置如下代码 可以让Textbox变为直线输入 <Style TargetType"TextBox"x:Key"UsernameTextBoxStyle"><Setter Property"Template"><Setter.Value><ControlTemplate TargetType"{x:Typ…...
多维数组与特殊矩阵:存储与压缩
多维数组与特殊矩阵:存储与压缩 一、多维数组的存储 (一)基本概念 多维数组是线性表的推广,例如二维数组可以看作是元素为一维数组的线性表,三维数组可以看作是元素为二维数组的线性表,以此类推。在内存…...
第三讲 架构详解:“隐语”可信隐私计算开源框架
目录 隐语架构 隐语架构拆解 产品层 算法层 计算层 资源层 互联互通 跨域管控 本文主要是记录参加隐语开源社区推出的第四期隐私计算实训营学习到的相关内容。 隐语架构 隐语架构拆解 产品层 产品定位: 通过可视化产品,降低终端用户的体验和演…...
springboot项目使用maven打包,第三方jar问题
springboot项目使用maven package打包为可执行jar后,第三方jar会被打包进去吗? 答案是肯定的。做了实验如下: 第三方jar的项目结构及jar包结构如下:(该第三方jar采用的是maven工程,打包为普通jar…...