【设计并实现一个满足 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.mzph.cn/news/643760.shtml

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

相关文章

【机器学习】模型的综合评判,备选模型和幸运模型

一、介绍 在快速发展的机器学习&#xff08;ML&#xff09;领域&#xff0c;模型的持续开发和部署对于技术进步和实际应用至关重要。这个生命周期的两个关键阶段是“候选模型”和“祝福模型”。对于任何参与机器学习的人来说&#xff0c;从数据科学家和工程师到依赖这些技术的企…

vue3 中组合键 command+Enter / shift+Enter / alt + Enter 实现换行,详细实现

vue3 中组合键实现换行 需求背景 有一个聊天室功能&#xff0c;采用输入框的形式&#xff0c;输入完毕使用Enter&#xff0c;可以直接进行发送。使用一些组合键 比如 commandEnter / shiftEnter / alt Enter … 可以实现换行操作。但现实的情况是&#xff0c;原生 Enter 天然…

C++ 之LeetCode刷题记录(十六)

&#x1f604;&#x1f60a;&#x1f606;&#x1f603;&#x1f604;&#x1f60a;&#x1f606;&#x1f603; 开始cpp刷题之旅。 依旧是追求耗时0s的一天。 100. 相同的树 给你两棵二叉树的根节点 p 和 q &#xff0c;编写一个函数来检验这两棵树是否相同。 如果两个树在…

MySQL InnoDB 底层数据存储

InnoDB 页记录Page Directory记录迁移 页 是内存与磁盘交互的基本单位&#xff0c;16kb。 比如&#xff0c;查询的时候&#xff0c;并不是只从磁盘读取某条记录&#xff0c;而是记录所在的页 记录 记录的物理插入是随机的&#xff0c;就是在磁盘上的位置是无序的。但是在页中…

K8S的图形化工具——rancher

rancher是一个开源的企业级多集群的K8S管理平台 hub.docker.com rancher和K8S区别&#xff1a; 都是为了容器的调度和编排系统&#xff0c;但是rancher不仅能够调度&#xff0c;还能管理K8S集群&#xff0c;自带监控&#xff08;普罗米修斯&#xff09; 即使不知道K8S是什么…

Vue-35、Vue中使用ref属性

1、ref属性 2、代码 <template><div id"app"> <!-- <img alt"Vue logo" src"./assets/logo.png">--><h1 v-text"msg" ref"title"></h1><button click"showDOM" ref&…

Dify学习笔记-基础介绍(一)

1、简介 Dify AI是一款强大的LLMOps&#xff08;Language Model Operations&#xff09;平台&#xff0c;专为用户提供便捷的人工智能应用程序开发体验。 该平台支持GPT系列模型和其他模型&#xff0c;适用于各种团队&#xff0c;无论是用于内部还是外部的AI应用程序开发。 它…

RKE快速搭建离线k8s集群并用rancher管理界面

转载说明&#xff1a;如果您喜欢这篇文章并打算转载它&#xff0c;请私信作者取得授权。感谢您喜爱本文&#xff0c;请文明转载&#xff0c;谢谢。 本文记录使用RKE快速搭建一套k8s集群过程&#xff0c;使用的rancher老版本2.5.7&#xff08;当前最新版为2.7&#xff09;。适用…

5分钟做自己的微信红包封面

文章目录 怎么制作自己的红包封面&#xff1f;开通红包封面的要求如下&#xff1a;收费情况制作具体网站&#xff1a;https://chatapi.onechat.fun/register?affYoU6 提交审核logo封面、挂件、气泡证明材料 发放红包封面其他 怎么制作自己的红包封面&#xff1f; 开通红包封面…

【第十五课】数据结构:堆 (“堆”的介绍+主要操作 / acwing-838堆排序 / 时间复杂度的分析 / c++代码 )

目录 关于堆的一些知识的回顾 数据结构&#xff1a;堆的特点 "down" 和 "up"&#xff1a;维护堆的性质 down up 数据结构&#xff1a;堆的主要操作 acwing-838堆排序 代码如下 时间复杂度分析 确实是在写的过程中频繁回顾了很多关于树的知识&…

【开源】基于JAVA+Vue+SpringBoot的教学过程管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 教师端2.2 学生端2.3 微信小程序端2.3.1 教师功能如下2.3.2 学生功能如下 三、系统展示 四、核心代码4.1 查询签到4.2 签到4.3 查询任务4.4 查询课程4.5 生成课程成绩 六、免责说明 一、摘要 1.1 项目介绍 基于JAVAVu…

一文读懂量化交易中的算法交易使用!

高频交易策略利用计算机&#xff0c;在人类交易者能够处理他们观察到的信息之前&#xff0c;根据以电子方式接收到的信息做出精心的决定来启动订单。 为通过挂单被动成交来追求更好的交易均价&#xff0c;随着人工智能&#xff0c;机器学习等技术的引入&#xff0c;逐渐演变为…

flink-java使用介绍,flink,java,DataStream API,DataSet API,ETL,设置 jobname

1、环境准备 文档&#xff1a;https://nightlies.apache.org/flink/flink-docs-release-1.17/zh/ 仓库&#xff1a;https://github.com/apache/flink 下载&#xff1a;https://flink.apache.org/zh/downloads/ 下载指定版本&#xff1a;https://archive.apache.org/dist/flink…

Git的管理操作

目录 前言 认识工作区、暂存区、版本库 小结&#xff1a; 使用场景--1&#xff1a; git log&#xff1a; 查看.git文件&#xff1a; 使用场景--2&#xff1a; git status&#xff1a; git diff&#xff1a; 进行提交&#xff1a; 总结&#xff1a; 版本回退 退…

Python函数调用的9大方法详解

概要 在Python中&#xff0c;函数是一种非常重要的编程概念&#xff0c;它们使得代码模块化、可重用&#xff0c;并且能够提高代码的可读性。本文将深入探讨Python函数调用的9种方法&#xff0c;包括普通函数、匿名函数、递归函数、高阶函数等&#xff0c;以及它们的应用示例。…

python内置函数有哪些?整理到了7大分类48个函数,都是工作中常用的函数

python内置函数 一、入门函数 1.input() 功能&#xff1a; 接受标准输入&#xff0c;返回字符串类型 语法格式&#xff1a; input([提示信息])实例&#xff1a; # input 函数介绍text input("请输入信息:") print("收到的数据是:%s" % (text))#输出…

k8s---pod的水平自动伸缩HPA

HPA&#xff1a;Horizontal Pod Autoscaling是pod的水平自动伸缩。是k8s自带的模块 pod占用CPU的比率到达一定的阈值会触发伸缩机制。 replication controller&#xff1a;副本控制器。控制pod的副本数 deployment controller&#xff1a;节点控制器。部署pod hpa控制副本的…

怎么他们都有开源项目经历|手把手教你参与开源

一、前言 大家好&#xff0c;这里是白泽。有一些同学提问&#xff0c;希望在自己的简历上增加一些有含金量的项目经历&#xff0c;最好能够去参与一些开源项目的开发&#xff0c;但由于对一个庞大的开源项目缺乏认知&#xff0c;难以着手。同时也担心自己能力不足&#xff0c;…

亚信安慧AntDB:AntDB-M元数据锁之锁的获取(三)

5 锁的获取 5.1 锁的强弱 当线程已经持有的锁比新申请的锁更强时&#xff0c;认为已经持有了锁&#xff0c;无需再对申请锁类型加锁。锁的强弱指持有的锁与其他锁的不兼容集合大小&#xff0c;集合相同锁相同&#xff0c;集合更大锁更强&#xff0c;否则无强弱关系。通过锁的…

php比较运算,强相等(===)弱相等(==)表

弱相等&#xff08;&#xff09; 符号为&#xff1a; 规则为&#xff1a;只比较值&#xff0c;不比较类型&#xff0c;只要值对就为true 样例&#xff1a;比较整型123和字符串"123"&#xff0c;运行结果给出了true 弱相等表&#xff1a;* 代表在 PHP 8.0.0 之前为…