数据结构之LRUCache

news2024/11/26 4:42:11

找往期文章包括但不限于本期文章中不懂的知识点:

个人主页:我要学编程(ಥ_ಥ)-CSDN博客

所属专栏:数据结构(Java版) 

目录

LRU Cache的概念 

LRU Cache的实现

模拟实现LRU Cache

相关练习 


 

LRU Cache的概念 

LRU是Least Recently Used的缩写,意思是最近最少使用,它是一种Cache替换算法。什么是Cache?狭义的Cache指的是位于CPU和主存间的快速RAM(random-access memory的缩写,即我们日常生活中所说的运行内存),通常它不像系统主存那样使用DRAM技术,而使用昂贵但较
快速的SRAM技术。广义上的Cache指的是位于速度相差较大的两种硬件之间,用于协调两者数据传输速度差异的结构。除了CPU与主存之间有Cache,内存与硬盘之间也有Cache,乃至在硬盘与网络之间也有某种意义上的Cache一称为lnternetl临时文件夹或网络内容缓存等。
Cache的容量有限,因此当Cache的容量用完后,而又有新的内容需要添加进来时,就需要挑选并舍弃原有的部分内容,从而腾出空间来放新内容。LRUCache的替换原则就是将最近最少使用的内容替换掉。其实,LRU译成最久未使用会更形象,因为该算法每次替换掉的就是一段时间内最久没有使用过的内容。

LRU Cache的实现

实现LRU Cache的方法和思路很多,但是要保持高效实现O(1)的put和get,那么使用双向链表和哈希表的搭配是最高效和经典的。使用双向链表是因为双向链表可以实现任意位置O(1)的插入和删除,使用哈希表是因为哈希表的增删查改也是O(1)。

在Java的集合类中有一个类和LRU Cache的实现类似——LinkedHashMap,其底层也是哈希表+双向链表。

每次使用get方法,访问数据后,会把数据放到当前双向链表的最后。
当accessOrder为true时,get方法和put方法都会调用recordAccess方法使得最近使用的Entry移到双向链表的末尾;当accessOrder为默认值false时,从源码中可以看出recordAccess方法什么也不会做。 

模拟实现LRU Cache

模式实现LRU Cache主要就是实现两个方法:put 和 get。

前期准备:

哈希表+双向链表:

    // 节点
    static class ListNode{
        public int key;
        public int val;
        public ListNode prev;
        public ListNode next;

        public ListNode(int key, int val) {
            this.key = key;
            this.val = val;
        }
    }
    // 这个哈希表中存放的是一个一个的节点,快速查找
    public HashMap<Integer, ListNode> hashMap;
    // 带头节点和尾节点便于修改,速度非常快
    public ListNode head;
    public ListNode tail;
    public int capacity; // LRU Cache容量
    public int usedSize; // 记录使用个数

    public LRUCache(int capacity) {
        this.hashMap = new HashMap<>();
        this.head = new ListNode(-1, -1);
        this.tail = new ListNode(-1, -1);
        this.head.next = this.tail;
        this.tail.prev = this.head;
        this.capacity = capacity;
    }

实现 put 方法:

思路: 首先,得在哈希表中查找是否存在该key。如果存在,只需要更新其 val值即可,然后再把这个节点移动到链表的尾部(先删除,在尾插);如果不存在,就新实例化一个节点,然后再尾插到链表尾部,接着usedSize++,检查容量是否满了。满了就得删除头结点(最近最久未被使用)

代码实现:

    public void put(int key, int val) {
        // 首先得查找hashmap中是否有这个元素
        ListNode node = hashMap.get(key);
        if (node == null) {
            ListNode newNode = new ListNode(key, val);
            hashMap.put(key, newNode); // 哈希表存一份
            usedSize++; // 个数++
            addToTail(newNode); // 把该节点移到链表末尾
            if (usedSize > capacity) {
                // 移除链表的头节点
                ListNode head = moveHead();
                // 将哈希表中的元素移除
                hashMap.remove(head.key);
                usedSize--;
            }
        } else {
            // 存在这个元素,更新val即可
            node.val = val; // 这里就已经把哈希表中的val更新了
            moveTail(node);
        }
    }

    private ListNode moveHead() {
        ListNode del = head.next;
        head.next = del.next;
        del.next.prev = head;
        return del;
    }

    private void moveTail(ListNode node) {
        // 先删除node节点,在尾插到末尾
        removeNode(node);
        addToTail(node);
    }

    private void addToTail(ListNode node) {
        node.next = tail;
        node.prev = tail.prev;
        tail.prev.next = node;
        tail.prev = node;
    }

    private void removeNode(ListNode newNode) {
        newNode.prev.next = newNode.next;
        newNode.next.prev = newNode.prev;
    }

实现 get 方法: 

思路: 直接在哈希表中查找。如果不存在,返回-1;如果存在,就返回其对应的val,并在此之前把这个节点移动到链表的尾部。

代码实现:

    public Integer get(int key) {
        if (hashMap.get(key) == null) {
            return -1;
        } else {
            // 把这个移到链表尾部
            moveTail(hashMap.get(key));
            return hashMap.get(key).val;
        }
    }

相关练习 

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) 的平均时间复杂度运行。

示例:

输入
["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

提示:

  • 1 <= capacity <= 3000
  • 0 <= key <= 10000
  • 0 <= value <= 105
  • 最多调用 2 * 105 次 get 和 put

思路一:就是直接手动实现一个LRU Cache,也就是我们模拟实现LRU Cache的代码。

思路二:在Java中LInkedHashMap是类似于LRU Cache的,因此我们直接用这个来写也是可以的。 

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);
    }

    // 注意:这个是用来判断是否需要移除元素的,因此一定要重写(默认是false)
    @Override
    protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
        return size() > capacity; 
    }
}

好啦!本期  数据结构之LRUCache 的学习之旅就到此结束啦!我们下一期再一起学习吧!

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

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

相关文章

FFmpeg的入门实践系列三(基础知识)

欢迎诸位来阅读在下的博文~ 在这里&#xff0c;在下会不定期发表一些浅薄的知识和经验&#xff0c;望诸位能与在下多多交流&#xff0c;共同努力 文章目录 前期博客一、音视频常用术语二、FFmpeg库的结构介绍三、FFmpeg的常用函数初始化封装格式编解码器相关 四、FFmpeg常用的数…

备战秋招60天算法挑战,Day23

题目链接&#xff1a; https://leetcode.cn/problems/counting-bits/ 视频题解&#xff1a; https://www.bilibili.com/video/BV1RE421w7C7/ LeetCode 338. 比特位计数 题目描述 给你一个整数 n &#xff0c;对于 0 < i < n 中的每个 i &#xff0c;计算其二进制表示中…

我店平台商业模式案例分析

我店平台是一家综合性的新消费平台&#xff0c;其商业模式结合了“互联网实体终端”的理念&#xff0c;通过数字化技术促进消费生态的升级和变革。以下是对该平台商业模式的概述&#xff1a; 平台定位 我店平台的目标是建立一个消费者、商家和平台三者都能从中受益的消费生态圈…

特斯拉电动卡车事故引发安全调查,汽车制造商电动车战略调整

特斯拉电动卡车事故引关注 周三&#xff0c;美国国家运输安全委员会宣布启动对特斯拉电动半挂卡车在加州高速上发生的事故及随后起火事件的全面调查。此次调查将联合加州公路巡警局共同进行&#xff0c;旨在查明事故原因&#xff0c;确保道路安全。事故发生在周一深夜&#xff…

性价比运动耳机有哪些?五大性价比运动耳机推荐!

作为一名资深的数码爱好者&#xff0c;我一直对各种新型耳机产品保持着浓厚的兴趣。最近&#xff0c;我因为很多运动爱好者都在询问什么耳机是比较适合运动的时候使用的&#xff0c;看了市面上的产品&#xff0c;开放式耳机无疑是一个不错的选择&#xff0c;它因为采用人体工学…

控制反转(IOC)——Spring第一个核心思想

控制反转概述 控制反转&#xff08;IoC&#xff09;是一种将对象的创建、组装和管理交给容器或框架来实现的编程思想。它可以减低对象之间的耦合度&#xff0c;提高代码的灵活性和模块化。控制反转的一种常见形式是依赖注入&#xff08;DI&#xff09;&#xff0c;即对象的依赖…

Godot模拟实现多人游戏平滑移动

最近几天接触到Godot,发现是一个很不错的免费开源游戏引擎,游戏本身实现了多人对战游戏之间同步功能(好像更适合于即时对战类,而且对战人数对计算机的性能要求高); 如果要实现一个大型的游戏,1个服务端(长期运行)对应N多个客户端(随时运行)需要怎么实现,不知道是不…

Rust到底值不值得学

【图书介绍】《Rust编程与项目实战》-CSDN博客 《Rust编程与项目实战》(朱文伟&#xff0c;李建英)【摘要 书评 试读】- 京东图书 (jd.com) Rust是近两年呼声比较高的一种新型开发语言&#xff0c;市场占有量并不大&#xff0c;但增长速度极为迅猛。 有人统计过&#xff0c;…

【访客管理系统】基于低代码+golang+人脸识别技术实现的访客管理系统

概述 访客预约-审批-核验流程 5个步骤&#xff1a;生成邀约 -> 访客填写信息 -> 企业员工审批 -> 访客获取凭证 -> 访客核验入园 访客核验方式 4种方式&#xff1a;二维码&#xff0c;身份证&#xff0c;人证核验&#xff0c;人脸识别 工作台 资料管理 园区管…

消息可靠性

若支付服务和mq之前网络链接失败/mq挂掉/交易服务挂掉,都有可以让支付订单无法更新,所以导致发送者不可靠,mq本身不可靠,消费者不可靠,消息延迟,针对以下问题 生产者可靠性 支付服务和mq之间有可能连不上,连不上怎么办,可以增加失败重连 配置文件中配置-重连 测试结果…

栈相关的算法

抽象&#xff1a; 不能处理的信息&#xff1a;暂时入栈。 能处理的信息&#xff1a;从栈里面选择信息&#xff0c;加工处理&#xff0c;并出栈。 一、括号匹配算法 左括号等待匹配&#xff0c;所以入栈等待匹配。 右边括号就要判断是否匹配&#xff0c;所以判断是否匹配并出…

如何设置PowerBI报告展示在屏幕的大小?

问题描述: 业务部门同事反馈&#xff0c;将开发的Power BI报告发布到Power BI服务站点后&#xff0c;用户通过不同尺寸的电子设备打开报告看到的效果不一样&#xff0c;如用PC打开报告可以在一屏中完整显示所有报告可视化组件如图&#xff1a; 但是用手机或者PAD类小尺寸电子设…

【好书推荐】值得深读的EMC参考书籍

以下排序不分先后&#xff0c;都是好书&#xff01; 书名&#xff1a;Electromagnetic Compatibility Engineering 作者&#xff1a;Henry W. Ott 这本书的讨论重点放在了经济高效的EMC设计上&#xff0c;并将数学的数量和复杂度保持在最低限度&#xff0c;辅以 250 多个带有…

【Qt笔记】Qt建立UDP通信

目录 一、前言 二、服务端 ①在项目配置文件中加入network模板 ②创立一个继承自QObject的UdpServer的类 ③构造函数的实现 ④读取信息函数的实现 三、客户端 ①绑定服务端的IP地址和端口号 ②发送数据报给服务端 四、实现效果 一、前言 UDP&#xff08;User Datagr…

流苏马兜铃Aristolochia fimbriata参考基因组

推荐官网&#xff0c;一个和无油樟一样没发生过全基因组复制事件的代表性物种。 文章同链接 如何获取mostly单拷贝和strictly单拷贝基因 Insights into angiosperm evolution, floral development and chemical biosynthesis from the Aristolochia fimbriata genome https:…

微服务CI/CD实践(一)环境准备及虚拟机创建

微服务CI/CD实践系列&#xff1a; 微服务CI/CD实践&#xff08;一&#xff09;环境准备及虚拟机创建 微服务CI/CD实践&#xff08;二&#xff09;gitlabs部署 微服务CI/CD实践&#xff08;三&#xff09;nexus3部署 微服务CI/CD实践&#xff08;四&#xff09;数据库,redis,n…

天猫商品评论API:获取商品热门评价与最新评价

天猫&#xff08;Tmall&#xff09;作为中国最大的B2C电商平台之一&#xff0c;提供了丰富的商品和服务。然而&#xff0c;天猫并没有直接公开一个通用的API来允许第三方开发者直接获取商品的热门评价或最新评价。这主要是出于保护用户隐私、防止恶意抓取和滥用数据等考虑。 不…

SpringIoCDI

前言&#x1f440;~ 上一章我们介绍了Spring MVC&#xff0c;今天介绍Spring核心功能之一IoC Spring到底是什么&#xff1f; Spring IoC 什么是 IoC 容器&#xff1f; IoC 介绍 DI 介绍 IoC详解 获取Bean对象的其他方式 Bean的存储 方法注解 Bean 扫描路径 DI详解 …

计算机网络——TCP协议与UDP协议详解(下)

一、TCP协议 1.1 TCP协议的报文 TCP全称为 "传输控制协议(Transmission Control Protocol")。人如其名&#xff0c;要对数据的传输进行一个详细的控制。我们先看其报文格式&#xff0c;如下图&#xff1a; TCP报文由以下几个字段组成&#xff1a; 源端口号和目标端口…

【第三版 系统集成项目管理工程师】第13章 监控过程组

持续更新。。。。。。。。。。。。。。。 【第三版】第十三章 监控过程组 13.1控制质量13.1.1主要输入1.项目管理计划-P4992.项目文件-P4993.批准的变更请求-P5004.可交付成果-P5005.工作绩效数据-P500 13.1.2主要工具与技术1.数据收集-P5002.数据分析-P5003.检查-P5014.测试/…