Leetcode面试经典150题-146.LRU缓存

news2024/12/23 7:37:33

解法都在代码里,不懂就留言或者私信,这个题大概率不会让你直接写代码,而是说以下思路,如果写代码这个题写出来基本就过了 

class LRUCache {
    /**首先我们得有缓存,get和put都是O(1)时间复杂度,我们常用的数据结构里应该只有Hashmap */
    Map<Integer, DoubleLinkedListNode> cache;
    int capacity;
    /**我们自定义了一个双向链表用于淘汰的操作*/
    DoubleLinkedList dll;
    public LRUCache(int capacity) {
        this.capacity = capacity;
        this.cache = new HashMap<>();
        this.dll = new DoubleLinkedList();
    }
    
    public int get(int key) {
        /**我们想想cache这个map为什么是Map<Integer, DoubleLinkedListNode>而不是Map<Integer, Integer> ,因为我们要操作链表*/
        if(!cache.containsKey(key)) {
            return -1;
        }
        DoubleLinkedListNode node = cache.get(key);
        dll.moveToTail(node);
        return node.val;
    }
    
    public void put(int key, int value) {
        /**往里放值的时候需要判断这个值在原来的缓存中存在不存在,如果存在只是一个缓存的更新操作,更新完之后把这个节点从链表中某个位置移动到尾部即可 */
        if(cache.containsKey(key)) {
            DoubleLinkedListNode node = cache.get(key);
            node.val = value;
            dll.moveToTail(node);
        } else {
            /**如果不存在的话需要判断缓存是不是已经满了,如果满了就涉及淘汰了,淘汰哪个?淘汰双向链表头部的,因为它是访问时间距离现在最远的*/
            if(cache.size() == capacity) {
                DoubleLinkedListNode head = dll.removeHead();
                cache.remove(head.key);
            }
            /**包装成节点 */
            DoubleLinkedListNode node = new DoubleLinkedListNode(key, value);
            /**放入map */
            cache.put(key, node);
            /**放入双向链表 */
            dll.addNode(node);
        }
    }

    /**定义双向链表节点和双向链表类*/
    static class DoubleLinkedListNode {
        /**属性有前驱和后继节点以及自己的值 */
        DoubleLinkedListNode pre;
        DoubleLinkedListNode next;
        int key;
        int val;
        public DoubleLinkedListNode(int key, int val) {
            this.key = key;
            this.val = val;
        }
    }

    static class DoubleLinkedList {
        /**对于双向链表来说,其实和单向链表没啥不一样的,都是有头有尾
        中间是双向还是单向靠节点的属性实现就行了,这里我也不写set get了,我们是写算法不是真实的项目,没必要的*/
        DoubleLinkedListNode head;
        DoubleLinkedListNode tail;
        /**加入节点是最基础的操作,这里就是加入节点,至于满不满,放在LRUCache里判断*/
        public void addNode(DoubleLinkedListNode node) {
            if(head == null) {
                /**还没有加入过节点,那加入了之后它既是头又是尾 */
                head = tail = node;
            } else {
                /**当前节点放在最后作为尾巴 */
                tail.next = node;
                /**千万不要忘了pre指针 */
                node.pre = tail;
                tail = node;
               
            }
        }
        /**LRU缓存的特点是哪个值的访问时间离现在越近,就是最热的应该保存,哪个离现在最远,应该就是缓存满了的时候被淘汰的
        我们这里双向链表用在LRU缓存里,需要有几个方法1. moveToTail用于某个key被访问了之后把它挪到尾部
        2.removeHead用于缓存满了之后淘汰(head放的是访问时间离现在最远的)
        removeHead需要有返回值,因为hashMap要同步删除*/
        public DoubleLinkedListNode removeHead() {
            /**强壮性校验,其实用不到,习惯了*/
            if(head == null) {
                return null;
            }
            /**拿到老的头部用于返回 */
            DoubleLinkedListNode oldHead = head;
            /**它的下一个节点作为新的头 */
            DoubleLinkedListNode next = head.next;
            /**断开和下一个节点的连接 */
            head.next = null;
            /**指认下个节点位头 */
            head = next;
            /**只有一个节点的时候next是不存在的,所以这里要判空 */
            if(head != null) {
                head.pre = null;
            }
            
            return oldHead;
        }
        /**某个数据被访问了之后变成热点数据的操作 */
        public void moveToTail(DoubleLinkedListNode node) {
            if(node == tail) {
                /**本来就在尾部,就别移动了把 */
                return;
            }
            /**如果是头部的话则涉及换头的操作*/
            if(node == head) {
                DoubleLinkedListNode next = node.next;
                /**不管if还是else里都有断开和后面节点连接的操作,放在最后的统一处理里 */
                head = next;
                head.pre = null;
            } else {
                /**不在尾部必有前驱,不在头部必有后继,把前驱和后继连一起*/
                DoubleLinkedListNode pre = node.pre;
                DoubleLinkedListNode next = node.next;
                pre.next = next;
                next.pre = pre;
            }
            /**最后进行通用的放在尾部的操作,放在尾部就把next置为null,两个原因:1尾部本来就没有后继 2.与原来的next断开连接 */
            node.next = null;
            tail.next = node;
            /**一共要注意pre指针 */
            node.pre = tail;
            tail = node;            
        }
    }
}

/**
 * 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);
 */

这个题我的目的就是写出来,不知道是不是最优解,欢迎指导

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

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

相关文章

公用事业公司与数据中心的电力协议推动未来增长

随着人工智能技术的迅猛发展&#xff0c;美国公用事业公司正在积极与数据中心运营商签订电力供应协议。这一趋势预计将显著提升这些公司的销售额和利润&#xff0c;并对未来几年的能源市场产生深远影响。 数据中心电力需求激增 根据高盛的报告&#xff0c;到2030年&#xff0c…

WMS助力企业数字化转型(六)

在当今数字化时代&#xff0c;仓库管理系统&#xff08;WMS&#xff09;作为推动企业数字化转型的重要工具&#xff0c;通过实时数据监控、自动化操作和智能分析&#xff0c;大幅提升了仓储管理的效率与精准度&#xff0c;为企业在供应链优化、库存控制和客户满意度方面带来了显…

Datawhale X 魔搭 AI夏令营 第四期魔搭-AIGC文生图方向Task2笔记

了解一下 AI生图技术 的能力&局限 对所有人来说&#xff0c;定期关注AI生图的最新能力情况都十分重要&#xff1a; 对于普通人来说&#xff0c;可以避免被常见的AI生图场景欺骗&#xff0c;偶尔也可以通过相关工具绘图 对于创作者来说&#xff0c;通过AI生图的工具可以快速…

GUI Agent with SFT 学习

grounding指的是基础训练&#xff0c;定位之类的意思&#xff0c;sft指的是监督微调&#xff0c;也就是用带有标签的数据集对与训练完毕的模型进行微调&#xff08;因为是带标签的&#xff0c;所以叫监督&#xff09; ui理解能力分为两个部分&#xff1a;Static UI understandi…

离线安装部署springboot+vue系统到服务器

注意&#xff1a;首先服务器会有多个网卡&#xff0c;这些服务器的网卡连接所需要的文件可能不是我们默认的ifcfg-eth0/ifcfgens33,可以试着切换一下服务器网线插入的接口&#xff0c;要保证服务器网线插入的接口和网卡对应的文件一致 说明&#xff0c;在一些政府&#xff08;保…

lvs的相关应用2

lvs 安装lvs 配置规则&#xff0c;查看所有的规则&#xff0c;如果已经配置好规则&#xff0c;重启之后就没了 [rootds01 ~]# ipvsadm -Ln IP Virtual Server version 1.2.1 (size4096) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forwa…

react的setState中为什么不能用++?

背景&#xff1a; 在使用react的过程中产生了一些困惑&#xff0c;handleClick函数的功能是记录点击次数&#xff0c;handleClick函数被绑定到按钮中&#xff0c;每点击一次将通过this.state.counter将累计的点击次数显示在页面上 困惑&#xff1a; 为什么不能直接写prevStat…

为什么要学习AI大模型?

AI大模型正在以惊人的速度改变着各行各业。正如移动互联网时代造就了无数成功的开发者&#xff0c;今天的大模型技术也为我们带来了前所未有的机遇。学习和掌握这项技术&#xff0c;不仅能让你站在行业前沿&#xff0c;还能为你的职业生涯带来巨大的回报。 01 企业为什么需要…

Linux shell编程学习笔记70: curl 命令行网络数据传输工具 选项数量雷人(下)

0 前言 curl是一款综合性网络传输工具&#xff0c;既可以上传也可以下载&#xff0c;支持HTTP、HTTPS、FTP等30余种常见协‍议。 Linux和Windows都提供了curl命令。 D:\>curl --help Usage: curl [options...] <url>-d, --data <data> HTTP POST da…

sql实战

这里写自定义目录标题 sql实战cmseasy daiqile全局污染 RCE限制16字符传入参数限制传入字符7个限制35字符&#xff0c;并过滤所有英文数字 sql实战 cmseasy 1、/lib/admin/admin.php和/lib/admin/tool/front_class.php源代码中发现&#xff0c;可以伪造IP并且传入ishtml1&…

Leetcode JAVA刷刷站(26)删除有序数组中的重复项

一、题目概述 二、思路方向 为了原地删除重复出现的元素&#xff0c;并保持元素的相对顺序一致&#xff0c;我们可以使用双指针的方法来解决这个问题。这种方法通常被称为“快慢指针”法。在这个问题中&#xff0c;快指针&#xff08;fast&#xff09;用于遍历数组&#xff0…

计算机的错误计算(六十一)

摘要 解释计算机的错误计算&#xff08;六十&#xff09;中的错误计算原因。 计算机的错误计算&#xff08;六十&#xff09;中的计算可以归纳为 因此&#xff0c;我们只需要分析该算式。 例1. 已知 分析如何计算 首先&#xff0c;一个数乘以一个2&#xff0c;一般不会…

[Megagon Labs] Annotating Columns with Pre-trained Language Models

Annotating Columns with Pre-trained Language Models 任务定义 输入&#xff1a;一张数据表&#xff0c;但没有表头&#xff0c;只有表中的数据。 输出&#xff1a;每一列数据的数据类型&#xff0c;以及两列数据之间的关系。 数据类型和数据关系都是由训练数据决定的固定…

docker部署Prometheus、Grafana

docker部署Prometheus 1、 拉取prometheus镜像 docler pull prom/prometheus 遇到问题&#xff1a;注意下科学上网。 2、将prometheus配置文件放在外面管理 prometheus.yml global:scrape_interval: 15sevaluation_interval: 15salerting:alertmanagers:- static_configs:-…

聚合平台项目之数据抓取

首先先记录一下我自己对这个数据抓取的一些心得&#xff1a; 数据抓取也就是常说的爬虫。 在我没真正去做的时候&#xff0c;我还想爬虫好高大上。 现在学完之后也就怯魅了 其实本质就是在自己的代码中模拟浏览器给后端发请求&#xff0c;后端收到响应之后&#xff0c;返回…

Redis知识进阶-私人定制组

Redis 目录 RedisRedis 简介关键特征Redis不同操作系统安装在Linux上的安装&#xff1a;在macOS上的安装&#xff1a;在Windows上的安装&#xff1a; Redis 数据结构及特点常用5种及示例&#xff1a;其他结构 主要功能总结 Redis 简介 Redis是一个开源的高性能键值对数据库&am…

酶促4+2和2+2环加成反应(有机合成与生物合成)-文献精读38

酶促42和22环加成反应&#xff1a;区域与立体选择性的理解与应用 01 有机合成 类似有机化学&#xff1a;狄尔斯–阿尔德反应 狄尔斯–阿尔德反应是[42]环加成反应中最具代表的&#xff0c;由共轭双烯与亲双烯体构建环己烯骨架的经典反应。反应有良好的立体、位置选择性。 该…

3.类和对象(中)

1. 类的默认成员函数 默认成员函数就是用户没有显式实现&#xff0c;编译器会自动生成的成员函数称为默认成员函数&#xff08;就是我们不写&#xff0c;编译器会默认生成一份&#xff09;。一个类&#xff0c;我们不写的情况下编译器会默认生成以下6个默认成员函数&#xff0…

江协科技STM32学习笔记(第09章 I2C通信)

第09章 I2C通信 9.1 I2C通信协议 9.1.1 I2C通信 串口通信没有时钟线的异步全双工的协议。 案例&#xff1a;通信协议设计&#xff1a; 某个公司开发了一款芯片&#xff0c;可以干很多事情,比如AD转换、温湿度测量、姿态测量等等。这个芯片里的众多外设也是通过读写寄存器来…

InCDE论文翻译

InCDE论文翻译 Towards Continual Knowledge Graph Embedding via Incremental Distillation 通过增量蒸馏实现持续知识图嵌入 Abstract 传统的知识图嵌入(KGE)方法通常需要在新知识出现时保留整个知识图(KG)&#xff0c;这会带来巨大的训练成本。为了解决这个问题&#xf…