LeetCode刷题之HOT100之LRU缓存

news2024/11/26 20:50:22

2024/6/21 酷暑难耐,离开空调我将不知道能否《活着》,昨天跑步感觉全身的热无法排出去,出门那种热浪一阵一阵打过来,一点风都舍不得给我。早早的来到实验室,也没多早,九点哈哈,做题啦!

1、题目描述

在这里插入图片描述

2、逻辑分析

刚看到这个题目,我看了好久,看不懂啥意思,然后去看了题解以及代码,打算跳过这题的想法在脑海里飘过三四次,好长啊代码。但是我还是看完了,就是一个redis缓存思想。该算法使用了哈希表和双链表的数据结构。LRUCache 的大致思路是使用哈希表(如 HashMap)和双链表(Doubly Linked List)来实现一个最近最少使用(Least Recently Used, LRU)缓存机制。
具体思路如下:
数据结构:
使用 HashMap 来存储键值对(key-value pairs),其中 key 是缓存项的键,value 是对应的缓存值以及该值在双链表中的节点引用。
使用双链表来维护缓存项的访问顺序。最近访问的项位于链表的头部,而最久未访问的项位于链表的尾部
初始化:
创建一个空的双链表,包含头节点(head)和尾节点(tail),它们互相指向对方,形成一个循环链表。
初始化 HashMap 用于存储键值对。
get(key) 操作:
HashMap 中查找键(key)对应的节点。
如果节点存在(即该键在缓存中),则将该节点从当前位置删除,并重新插入到链表的头部,以表示该键最近被访问过。
返回该节点的值。
如果节点不存在,则返回 -1 表示未找到。
put(key, value) 操作:
HashMap 中查找键(key)对应的节点。
如果节点存在(即该键已经在缓存中),则更新该节点的值为新的 value,并将该节点从当前位置删除,重新插入到链表的头部。
如果节点不存在(即该键不在缓存中),则需要创建一个新节点,将新节点插入到链表的头部,并在 HashMap 中添加键值对。
如果此时缓存已满(即缓存中元素的数量超过了设定的容量),则需要删除链表尾部的节点,并在 HashMap 中移除对应的键值对,以腾出空间。
维护双链表:
当有新节点插入到链表的头部时,需要更新头节点和新节点的 prevnext 指针。
当有节点从链表中删除时,需要更新该节点的前一个节点和后一个节点的 prevnext 指针。
通过这种方式,LRU 缓存能够快速地访问最近使用过的数据,并在必要时淘汰最久未使用的数据,从而保持缓存的有效性。

3、代码演示

public class LRUCache {
    // 双链表节点类
    class DLinkedNode {
        int key;
        int value;
        DLinkedNode prev;
        DLinkedNode next;
        // 构造无参数的DLinkedNode实例(通常用于初始化头尾节点)
        public DLinkedNode(){}
        // 构造带有key和value的DLinkedNode实例
        public DLinkedNode(int _key, int _value){key = _key; value = _value;}
    }

    // 使用HashMap存储key到DLinkedNode的映射
    private Map<Integer, DLinkedNode> cache = new HashMap<Integer, DLinkedNode>();
    // 当前缓存中元素的数量 
    private int size;
    // 缓存的最大容量
    private int capacity;
    // 双链表的头尾节点,用于维护最近最少使用(LRU)顺序
    private DLinkedNode head, tail;

    // 构造函数,初始化LRUCache
    public LRUCache(int capacity) {
        this.size = 0;
        this.capacity = capacity;
        // 初始化头尾节点,它们之间互相指向,构成一个空链表
        head = new DLinkedNode();
        tail = new DLinkedNode();
        head.next = tail;
        tail.prev = head;
    }
    
    // 根据key获取缓存值
    public int get(int key) {
        DLinkedNode node = cache.get(key);
        // 如果节点不存在,返回-1 
        if(node == null){
            return -1;
        }
        // 节点存在,将访问过的节点移动到头部
        moveToHead(node);
        // 返回节点的值
        return node.value;

    }
    
    // 将key-value对放入缓存
    public void put(int key, int value) {
        DLinkedNode node = cache.get(key);
        // 如果节点不存在,创建一个新的节点并放入缓存
        if(node == null){
            DLinkedNode newNode = new DLinkedNode(key, value);
            cache.put(key, newNode);
            // 将新节点添加到头部
            addToHead(newNode);
            // 缓存元素数量加1
            size++;
            // 如果超过容量,删除尾部的节点
            if(size > capacity){
                DLinkedNode tail = removeTail();
                // 从HashMap中移除该节点 
                cache.remove(tail.key);
                // 缓存元素数量减1
                size--;
            }
            // 如果节点已存在,更新节点的值并移动到头部
        }else{
            node.value = value;
            moveToHead(node);
        }


    }

    // 将节点添加到双链表的头部
    private void addToHead(DLinkedNode node){
        node.prev = head;
        node.next = head.next;
        head.next.prev = node;
        head.next = node;
    }
    // 从双链表中移除一个节点
    private void removeNode(DLinkedNode node){
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }

    // 将一个节点从当前位置移动到双链表的头部
    private void moveToHead(DLinkedNode node){
        removeNode(node);
        addToHead(node);
    }
    
    // 移除并返回双链表的尾部节点
    private DLinkedNode removeTail(){
        DLinkedNode res = tail.prev;
        removeNode(res);
        return res;
    }
}

4、复杂度分析

  • 时间复杂度O(1)。get和put都只需要O(1)的时间。
  • 空间复杂度O(capacity)。capacity为最大缓存容量。

拜拜啦,我知道这题下次见到还是不能完整敲出来滴,那就希望下下次可以叭,再见!

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

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

相关文章

Linux【实操篇-文件目录类命令】

05【实操篇-文件目录类命令】 1.pwd 显示当前工作目录的绝对路径 pwd:print working directory 打印工作目录 到现在为止&#xff0c;我们还不知道自己在系统的什么地方。在浏览器上&#xff0c;我们能够通过导航栏上的url&#xff0c;了解到自己在互联网上的具体坐标。相似的…

GNSS是什么意思?探索全球导航卫星系统的应用与发展

GNSS是什么意思&#xff1f;GNSS是全球导航卫星系统&#xff08;Global Navigation Satellite System&#xff09;的缩写&#xff0c;它借助于多颗位于地球轨道上的卫星&#xff0c;通过接收和处理卫星信号来实现高精度的全球定位和导航。GNSS系统不仅包括美国的GPS&#xff08…

C# 语言在AGI 赛道上能做什么

自从2022年11月OpenAI正式对外发布ChatGPT依赖&#xff0c;AGI 这条赛道上就挤满了重量级的选手&#xff0c;各大头部公司纷纷下场布局。原本就在机器学习、深度学习领域占据No.1的Python语言更是继续稳固了自己AI一哥的位置。凭借着Microsoft 和 OpenAI 长期以来一直是紧密相连…

CVPR 2024 以物体为中心的多感知具身大语言模型

CVPR 2024发表了关于多感知以对象为中心的具身大型语言模型&#xff08;MultiPLY&#xff09;的研究论文&#xff0c;该模型在3D环境中进行编码和交互。 提出MultiPLY是一个多感知的具身大型语言模型&#xff08;LLM&#xff09;&#xff0c;能够将视觉、听觉、触觉和温度等多…

【netty】三万字详解!JAVA高性能通信框架,关于netty,看这一篇就够了

目录 1.概述 2.hello world 3.EventLoop 4.channel 4.1.同步 4.2.异步 4.3.调试 4.4.关闭 4.5.为什么要用异步 5.future 6.promise 7.pipeline 8.byteBuf 8.1.创建 8.2.内存模式和池化 8.2.1.内存模式 8.2.2.池化 8.3.组成 8.4.操作 8.4.1.读写 8.4.2.释放…

钢琴块小游戏(附源码)

代码结构 app.png是游戏运行主界面的图片&#xff08;可以加载自己喜欢的主界面图片&#xff09; Assets文件夹里面装的是一些需要用到的游戏图片 全部都可以替换为自己喜欢的图片 Fonts里面装的是 Sounds文件夹里面装的是 一 . 主程序代码 1.运行这个代码使得游戏开始 2.主界面…

深入理解Vue3.js响应式系统设计之调度执行

如果您觉得这篇文章有帮助的话&#xff01;给个点赞和评论支持下吧&#xff0c;感谢~ 作者&#xff1a;前端小王hs 阿里云社区博客专家/清华大学出版社签约作者/csdn百万访问前端博主/B站千粉前端up主 此篇文章是博主于2022年学习《Vue.js设计与实现》时的笔记整理而来 书籍&a…

Linux 一键部署 Nginx1.26.1 + ModSecurity3

前言 ModSecurity 是 Apache 基金会的一个开源、高性能的 Web 应用程序防火墙(WAF),它提供了强大的安全规则引擎,用于检测和阻止各种攻击行为,如 SQL 注入、XSS 跨站点脚本攻击等。而 nginx 是一个高性能的 Web 服务器,常用于处理大量的并发请求,具有很高的负载均衡能力…

知网期刊《新课程导学》投稿要求及收稿方向

知网期刊《新课程导学》投稿要求及收稿方向 知网期刊《新课程导学》作为一份专注于教育领域的学术期刊&#xff0c;一直以来都致力于为广大学术研究者提供一个高质量、高水平的学术交流平台。为了保证期刊的学术质量&#xff0c;编辑部对投稿要求和收稿方向有着严格的规定。 首…

Postman接口测试详解与进阶

Postman是一个功能强大的接口测试工具&#xff0c;它主要用于模拟用户发起的各类HTTP请求&#xff0c;从而验证响应中的结果数据是否和预期值相匹配。以下是关于Postman的详细介绍&#xff1a; Postman是一个功能全面、使用便捷、支持多种HTTP请求类型、提供丰富的测试数据和配…

铝型材挤压车间的数字孪生应用

图扑利用数字孪生技术&#xff0c;在铝型材挤压车间实现了生产线的全方位实时监控和优化。通过高精度三维建模和数据可视化&#xff0c;提升了效率和管理透明度&#xff0c;促进了智能制造和资源配置的优化。

【2024亲测无坑】在Centos.7虚拟机上安装Oracle 19C

目录 一、安装环境准备 1、linux虚拟机安装 2、虚拟机快照 3、空间检查&软件上传 二、Oracle软件安装 1.preinstall安装及其他配置准备 2.oracle安装 三、数据库实例的安装 1.netca——网络配置助手 2.dbca——数据库配置助手 四、ORACLE 19C 在linux centos 7上…

c++qt合并两张灰度图像

需求&#xff1a;将两张尺寸相同的灰度图像进行合并&#xff0c;合并后的图像&#xff0c;每个像素点灰度值为两张原图对应像素点灰度值之和。若超过255&#xff0c;则最大为255。 方法一&#xff1a; 将图像读取为cv::Mat&#xff0c;再调用opencv的cv::add方法&#xff0c;进…

Electron快速入门(一):用VS Code快速创建html+js+css编写的项目

创建一个文件夹&#xff08;例如&#xff1a;start或者create-electron 都是小写英文字母有的插件才不会报错&#xff09;&#xff0c;并进入该文件夹&#xff0c;打开 vscode创建3个文件&#xff1a; 1. 名为 main.js 的文件是主进程 // main.js//用于控制应用程序寿命和创建…

网络安全的双刃守护:揭秘双算法SSL证书的智慧盾牌

双算法SSL证书&#xff0c;这一科技与智慧的结晶&#xff0c;如同夜空中最亮的双子星&#xff0c;照亮了数据传输的幽径。它不仅继承了传统SSL证书的精髓&#xff0c;确保信息在传递过程中的私密与完整&#xff0c;更创新性地融合了两种顶尖加密算法——RSA与SM2&#xff0c;犹…

gateway整合sentinel限流

官方文档&#xff1a;https://github.com/alibaba/Sentinel/wiki/%E7%BD%91%E5%85%B3%E9%99%90%E6%B5%81 从 1.6.0 版本开始&#xff0c;Sentinel 提供了 Spring Cloud Gateway 的适配模块&#xff0c;可以提供两种资源维度的限流&#xff1a; route 维度&#xff1a;即在 Spr…

STM32的通用定时器中断编程

如果遇到需要单片机产生严格时序的场景&#xff08;比如DAC输出特定模拟信号&#xff0c;GPIO口控制模拟开关&#xff09;&#xff0c;延时函数可能就无法胜任了。最近在工作时公司上级教会了我使用“门票”思维&#xff08;中断标志位)编写单片机裸机程序&#xff0c;今天写一…

预制舱变电站高压室巡检机器人系统

一、背景 预制舱变电站高压室由于空间狭小、设备紧凑&#xff0c;传统的巡检方式往往需要人工进入高压室进行巡检&#xff0c;不仅存在安全风险&#xff0c;而且巡检效率低下&#xff0c;难以满足日益增长的电力设备运维需求。 二、预制舱高压室巡检机器人系统 预制舱高压室巡…

QT MQTT (二)编译与集成

一、QT MQTT 提供 MQTT 客户端服务的 Qt 专用库基于标准化发布 / 订阅协议&#xff0c;用于在设备和组件之间可靠地共享数据。MQTT 是为保证状态正确性、满足高安全标准和交换最小数据而设计的协议&#xff0c;因此被广泛应用于各种分布式系统和物联网解决方案中。 Qt开发MQT…

【SAP Abap】一条SQL语句实现支持报表项配置的财务报表

【SAP Abap】一条SQL语句实现支持报表项配置的财务报表 1、业务背景2、配置项特殊处理3、实现方式&#xff08;Hana Studio SQL语句&#xff09;4、实现方式&#xff08;Abap OpenSQL语句&#xff09;5、总结 1、业务背景 在财务三大报表之外&#xff0c;业务需要使用类似的科…