【设计并实现一个满足 LRU (最近最少使用) 缓存约束的数据结构】

文章目录

  • 一、什么是LRU?
  • 二、LinkedHashMap 实现LRU缓存
  • 三、手写LRU


一、什么是LRU?

LRU是Least Recently Used的缩写,意为最近最少使用。它是一种缓存淘汰策略,用于在缓存满时确定要被替换的数据块。LRU算法认为,最近被访问的数据在将来被访问的概率更高,因此它会优先淘汰最近最少被使用的数据块,以给新的数据块腾出空间。

如图所示:

在这里插入图片描述

  1. 先来3个元素进入该队列
    在这里插入图片描述

  2. 此时来了新的元素,因为此时队列中每个元素的使用的次数都相同(都是1),所以会按照LFU的策略淘汰(即淘汰掉最老的那个)
    在这里插入图片描述

  3. 此时又来了新的元素,而且是队列是已经存在的,就会将该元素调整为最新的位置。
    在这里插入图片描述

  4. 如果此时又来了新的元素,还是”咯咯“,由于”咯咯“已经处于最新的位置,所以大家位置都不变。
    在这里插入图片描述

  5. 同理,一直进行上述的循环


二、LinkedHashMap 实现LRU缓存

以力扣的算法题为例子:


力扣146. LRU 缓存


在 jdk 官方的介绍中可以看出,该数据结构天生适合实现 LRU。

在这里插入图片描述

代码示例一:


/*** 利用继承 LinkedHashMap 的方式实现LRU缓存*/class LRUCache extends LinkedHashMap<Integer, Integer> {// 缓存容量private int capacity;public LRUCache(int capacity) {// 初始化super(capacity, 0.75f, true);this.capacity = capacity;}public int get(int key) {return super.getOrDefault(key, -1);}public void put(int key, int value) {super.put(key, value);}@Overrideprotected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {// 重写比较方法return super.size() > capacity;}
}/*** Your LRUCache object will be instantiated and called as such:* LRUCache obj = new LRUCache(capacity);* int param_1 = obj.get(key);* obj.put(key,value);*/

代码示例二:

/*** 利用 LinkedHashMap 特点的方式实现LRU缓存*/class LRUCache {// 额定容量private final int CAPACITY;// 使用 LinkedHashMap 的有序排重特点达到要求private final Map<Integer, Integer> map;public LRUCache(int capacity) {// 初始化CAPACITY = capacity;map = new LinkedHashMap<>(CAPACITY);}public int get(int key) {Integer value = map.get(key);if (value != null) {// 存在// 1. 先删除旧的map.remove(key);// 2. 再添加回去,同时更新了 key 的位置和 valuemap.put(key, value);// 3. 返回结果return value;} else {// 不存在return -1;}}public void put(int key, int value) {if (map.size() == CAPACITY) {// 达到最大容量if (!map.containsKey(key)) {// 不存在,就删除头Integer head = map.keySet().iterator().next();map.remove(head);} else {// 存在,就删除自身map.remove(key);}} else {// 还有剩余空间,删除自身map.remove(key);}// 添加新的或 key 更新后的 valuemap.put(key, value);}
}

三、手写LRU

代码示例如下:

/*** https://leetcode.cn/problems/lru-cache/description/* 手写 LRU 缓存* Map + 双向链表*/
class LRUCache {// Map 负责查找,构建一个虚拟的双向链表,它里面安装的就是一个个 Node 节点,作为数据载体。// 参考 HashMap 中的存储方式,使用内部 Node 维护双向链表// 1. 构造 Node 节点作为数据载体public static class Node<K, V> {public K key;public V value;public Node<K, V> prev;public Node<K, V> next;public Node() {this.prev = this.next = null;}public Node(K key, V value) {this.key = key;this.value = value;this.prev = this.next = null;}}// 2. 构造一个双向队列,里面存放着 Node 节点// 队头元素最新,队尾元素最旧static class DoubleLinkedList<K, V> {Node<K, V> head;Node<K, V> tail;// 2.1 构造方法public DoubleLinkedList() {head = new Node<>();tail = new Node<>();// 头尾相连head.next = tail;tail.prev = head;}// 2.2 添加到头(头插)public void addHead(Node<K, V> node) {node.next = head.next;node.prev = head;head.next.prev = node;head.next = node;}// 2.3 删除节点public void removeNode(Node<K, V> node) {node.next.prev = node.prev;node.prev.next = node.next;node.next = null;node.prev = null;}// 2.4 获取最后一个节点public Node getLast() {return tail.prev;}}private final int cacheSize;Map<Integer, Node<Integer, Integer>> map;// MapDoubleLinkedList<Integer, Integer> doubleLinkedList;// 双向链表public LRUCache(int capacity) {this.cacheSize = capacity;map = new HashMap<>();doubleLinkedList = new DoubleLinkedList<>();}public int get(int key) {if (map.containsKey(key)) {// 命中,更新双向链表Node<Integer, Integer> node = map.get(key);// 先删除双向链表中的节点doubleLinkedList.removeNode(node);// 再添加到头部doubleLinkedList.addHead(node);return node.value;} else {// 未命中,返回 -1return -1;}}public void put(int key, int value) {if (map.containsKey(key)) {// 更新双向链表Node<Integer, Integer> node = map.get(key);// 新值替换老值,再放回node.value = value;map.put(key, node);// 先删除双向链表中的节点doubleLinkedList.removeNode(node);// 再添加到头部doubleLinkedList.addHead(node);} else {if (map.size() == cacheSize) {// 超出容量,删除双向链表的最后一个节点Node lastNode = doubleLinkedList.getLast();map.remove(lastNode.key);doubleLinkedList.removeNode(lastNode);}// 新增节点Node<Integer, Integer> newNode = new Node<>(key, value);map.put(key, newNode);doubleLinkedList.addHead(newNode);}}
}

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

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

相关文章

多输入多输出 | Matlab实现SSA-CNN麻雀算法优化卷积神经网络多输入多输出预测

多输入多输出 | Matlab实现SSA-CNN麻雀算法优化卷积神经网络多输入多输出预测 目录 多输入多输出 | Matlab实现SSA-CNN麻雀算法优化卷积神经网络多输入多输出预测预测效果基本介绍模型背景程序设计参考资料 预测效果 基本介绍 Matlab实现SSA-CNN麻雀算法优化卷积神经网络多输入…

cmd输入python直接弹出windows应用商店

明明已经安装好了python&#xff0c;并且也确认配置好了python的环境变量&#xff0c;但是在cmd里输入python后&#xff0c;直接弹出windows商店&#xff0c;python获取界面&#xff0c;其实只需要关闭系统里的应用执行别名设置&#xff0c;最近出来的电脑系统里是自带开启了py…

.NET国产化改造探索(六)、银河麒麟操作系统中安装多个.NET版本

随着时代的发展以及近年来信创工作和…废话就不多说了&#xff0c;这个系列就是为.NET遇到国产化需求的一个闭坑系列。接下来&#xff0c;看操作。 上一篇文章介绍了如何在银河麒麟操作系统上&#xff0c;使用Nginx.NET程序实现自启动。本文介绍下如何在一个环境中&#xff0c;…

【React】组件性能优化、高阶组件

文章目录 React性能优化SCUReact更新机制keys的优化render函数被调用shouldComponentUpdatePureComponentshallowEqual方法高阶组件memo 获取DOM方式refs如何使用refref的类型 受控和非受控组件认识受控组件非受控组件 React的高阶组件认识高阶函数高阶组件的定义应用一 – pro…

Object.prototype.toString.call个人理解

文章目录 这段代码的常见用处参考文献&#xff1a; 拆分理解1、Object.prototype.toString小问题参考文献&#xff1a; 2、call函数的作用参考文献 3、继续深入一些&#xff08;这部分内容是个人理解&#xff0c;没有明确文献支撑&#xff09; 这段代码的常见用处 Object.prot…

云轴科技ZStack位列IDC云系统软件市场教育行业TOP2

近日&#xff0c;全球IT市场研究和咨询公司IDC发布 《中国云系统软件市场跟踪报告2023H1》 ZStack作为产品化的云基础软件提供商 位居云系统软件市场第一梯队 市场份额位列独立云厂商*第一 营收同比增速最快 教育行业TOP2 在教育行业&#xff0c;云计算已成为教育行业信息化的…

Chatgpt+Comfyui绘图源码说明及本地部署文档

其他文档地址&#xff1a; ChatgptComfyui绘图源码运营文档 ChatgptComfyui绘图源码线上部署文档 一、源码说明 1、源码目录说明 app_home&#xff1a;app官网源码chatgpt-java&#xff1a;管理后台服务端源码、用户端的服务端源码chatgpt-pc&#xff1a;电脑网页前端源码cha…

机器学习 | 掌握Matplotlib的可视化图表操作

Matplotlib是python的一个数据可视化库&#xff0c;用于创建静态、动态和交互式图表。它可以制作多种类型的图表&#xff0c;如折线图、散点图、柱状图、饼图、直方图、3D 图形等。以渐进、交互式方式实现数据可视化。当然博主也不能面面俱到的讲解到所有内容&#xff0c;详情请…

Qt 拖拽事件示例

一、引子 拖拽这个动作,在桌面应用程序中是非常实用和具有很友好的交互体验的。我们常见的譬如有,将文件拖拽到某个窗口打开,或者拖拽文件到指定位置上传;在绘图软件中,选中某个模板、并拖拽到画布上,画布上变回绘制该模板的图像… 诸如此类,数不胜数。 那么,在Qt中我…

初识k8s(概述、原理、安装)

文章目录 概述由来主要功能 K8S架构架构图组件说明ClusterMasterNodekubectl 组件处理流程 K8S概念组成PodPod控制器ReplicationController&#xff08;副本控制器&#xff09;ReplicaSet &#xff08;副本集&#xff09;DeploymentStatefulSet &#xff08;有状态副本集&#…

Spring Security工作原理(三)

在认证之间保存请求 如处理安全异常中所示,当请求没有认证且需要认证资源时,需要保存请求以便在认证成功后重新请求受保护的资源。在Spring Security中,这是通过使用RequestCache实现来保存HttpServletRequest来实现的。 RequestCache HttpServletRequest被保存在Request…

【nginx实战】nginx正向代理、反向代理、由反向代理实现的负载均衡、故障转移详解

文章目录 一. 正向代理与反向代理的概念二. Nginx服务器的正向代理服务1. Nginx服务器正向代理服务的配置的3个指令1.1. resolver指令1.2. resolver_timeout指令1.3. proxy_pass指令 2. Nginx服务器正向代理服务的使用 三. Nginx服务器的反向代理服务1. 反向代理的基本指令1.1.…

JS在当前时间的基础上减10个月或者20个月

需求&#xff1a;调用接口默认传当前时间&#xff0c;如2024-01&#xff0c;点击分页第几页都是传其上一页的最后一条日期的减一月份 思路&#xff1a;点击第几页&#xff0c;减多少&#xff0c;比如点击第二页减10月&#xff0c;点击第三页减20月&#xff0c;代码如下&#xf…

大数据学习之Flink算子、了解DataStream API(基础篇一)

DataStream API &#xff08;基础篇&#xff09; 注&#xff1a; 本文只涉及DataStream 原因&#xff1a;随着大数据和流式计算需求的增长&#xff0c;处理实时数据流变得越来越重要。因此&#xff0c;DataStream由于其处理实时数据流的特性和能力&#xff0c;逐渐替代了DataSe…

ZXing开源库生成二维码

引言 二维码&#xff08;QR Code&#xff09;作为一种快速、高容量、高密度的矩阵条码&#xff0c;已经在各行各业得到广泛应用。ZXing&#xff08;Zebra Crossing&#xff09;是一款由Google开源的Java二维码生成和解析库&#xff0c;提供了丰富的功能和易于使用的API。本篇博…

C# Cad2016二次开发选择csv导入信息(七)

//选择csv导入信息 [CommandMethod("setdata")] //本程序在AutoCAD的快捷命令是"DLLLOAD" public void setdata() {Microsoft.Win32.OpenFileDialog dlg new Microsoft.Win32.OpenFileDialog();dlg.DefaultExt ".csv";// Display OpenFileDial…

什么样的宣传才能对消费者起效?

品牌离不开宣传&#xff0c;宣传又直接面向消费者&#xff0c;然后面对铺天盖地的宣传&#xff0c;除了从业人员&#xff0c;相信大部分用户都会有抵触心理&#xff0c;今天媒介盒子就来和大家聊聊&#xff0c;什么样的宣传能够提高消费者的接受度&#xff0c;让宣传不白宣传。…

RabbitMQ中交换机的应用 ,原理 ,案例的实现

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是平顶山大师&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的博客专栏《RabbitMQ中交换机的应用及原理&#xff0c;案…

TCP服务器最多支持多少客户端连接

目录 一、理论数值 二、实际部署 参考 一、理论数值 首先知道一个基础概念&#xff0c;对于一个 TCP 连接可以使用四元组&#xff08;src_ip, src_port, dst_ip, dst_port&#xff09;进行唯一标识。因为服务端 IP 和 Port 是固定的&#xff08;如下图中的bind阶段&#xff0…

Pytest中conftest.py的用法

Pytest中conftest.py的用法 ​ 在官方文档中&#xff0c;描述conftest.py是一个本地插件的文件&#xff0c;简单的说就是在这个文件中编写的方法&#xff0c;可以在其他地方直接进行调用。 注意事项 只能在根目录编写conftest.py 插件加载顺序在搜集用例之前 基础用法 这里…