力扣hot100:146. LRU 缓存

力扣hot100:146. LRU 缓存
在这里插入图片描述
听说华为实习笔试考了这题

如何使得插入操作时 O ( 1 ) O(1) O(1)呢?我们需要维护一个时间的长短,以便于取出离现在最长的时间,这个时间比较容易实现,我们维护一个time表示当前时间,从0开始,然后在使用的一些关键字里面,当一个关键字使用的时间越小,那么它越久未被使用,我们只需要维护一个关键字使用的最小值就行,并且还需要有更新操作。

  1. 如果我们使用优先队列,那么插入一次需要 O ( l o g n ) O(logn) O(logn),不满足要求
  2. 如果我们排序,那么排序一次需要 O ( n l o g n ) O(nlogn) O(nlogn),不满足要求
  3. 我们使用双向链表维护时间递增序列呢?使用哈希表快速查找结点
    • 答案是可行的!因为time是递增的,我们更新中间结点的时间,则必然会将这个结点移动到链表末尾,因此是 O ( 1 ) O(1) O(1)的。这样做我们甚至可以连时间都不用保存了

由于结点和值都是通过key来查询的,而查询只需要判断key,因此我们可以只使用一个哈希表,来同时维护这两个信息。
在这里插入图片描述

class LRUCache {
public:LRUCache(int capacity) {this->capacity = capacity;dummy = new List;end = dummy;}int get(int key) {if(mp.count(key) == 1){put_to_end(key);return mp[key].first;}return -1;}void put(int key, int value) {if(mp.count(key) == 1){mp[key].first = value;put_to_end(key);return;}//判断是否超出容量if(size == capacity){List * temp = dummy->next;dummy->next = temp->next;if(temp->next) temp->next->pre = dummy;mp.erase(temp->key);if(temp == end) end = temp->pre;delete temp;}else ++size;//插入全新元素List * lst = new List;lst->key = key;mp[key] = {value,lst};//放到末尾lst->pre = end;end->next = lst;end = lst;return;}
private:struct List{List * pre = nullptr;List * next = nullptr;int key;//该key用于删除头结点};void put_to_end(int key){List * access = mp[key].second;if(access == end) return;access->pre->next = access->next;if(access->next) access->next->pre = access->pre;access->next = nullptr;access->pre = end;end->next = access;end = access;return;}
private:unordered_map<int,pair<int,List *>> mp;int capacity;int size = 0;List * dummy;List * end;
};

需要注意的点:

  1. 容量为1,需要删除的结点可能就在队列末尾,因此需要和end比较
  2. 最新被使用的结点可能就在队列末尾,因此需要和end比较
  3. 更新新结点时,注意end需要指向它
  4. 除了使用伪首部之外,还可以使用伪尾部!这样对尾部的判断也减少了许多。

写完之后可以再思考一个,LRU:

  • LRU是一个最近最少使用的缓存机制,实现时,本质上,我们将最近使用的放到了双向链表的尾部,而最久没有使用的就再链表头,一旦我们需要这个值的时候,我们只需要使用哈希表快速查找到值在双向链表中的位置,再把它放到链表尾部即可。
  • 实际上我们这相当于维护了一个单调序列,并且能够快速修改它的值并更新位置,我们每次只取头部使用,这似乎比优先队列还快。但实际上他们的使用场景不一样,思考一下可以发现双向链表维护的单调序列,每次更新内部结点的值后它必然被更新到最大值,会被放在最后面,而不会更新后还在中间,如果更新后的值并不一定是最值,则这种方法就不适用了。

既然这是一个面试常考的题,那么写完后可以记住:LRU 缓存机制可以通过哈希表辅以双向链表实现,我们用一个哈希表和一个双向链表维护所有在缓存中的键值对。

在这里插入图片描述

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

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

相关文章

C#压缩单个文件

1、压缩方法 /// <summary> /// 压缩 /// </summary> /// <param name"source">源目录</param> /// <param name"s">ZipOutputStream对象</param> public static void Compress(string source, ZipOutputStream s) {…

【Telemac】Telemac相关报错记录

文章目录 1.下载BlueKenue后缀为man解决办法2.运行Telemac项目提示Fortran报错解决办法3.jupyter闪退或jupyter 不是内部或外部命令,也不是可运行的程序或批处理文件。解决办法4.ERROR: Failed to post close command error 1717解决办法1.下载BlueKenue后缀为man BlueKenue官…

kettle学习之子映射组件

映射组件就跟java中的函数方法一样&#xff0c;类似一个子流程。 练习开始 根据数据库表中的id查询出想要的字段&#xff0c;并把字段存到excel表中 一、表输入 二、子映射 映射输入规范&#xff0c;类似java方法中的形参 name vsxcd是方法返回的参数 三、excel输出 运行结果…

【HDFS】FSImage加载过程之整体流程一览

本文总结了加载FSImage的四个或者说三个主要步骤,并进行了源码逐行分析。 Loader#loadInternal方法里,定义了加载fsimage文件的整理流程。 第一步: loadSummary。 从fsimage文件中把FileSummary给加载出来。 // RandomAccessFile raFile, fsimage文件 FileSummary summa…

[处理器芯片]-1 概要介绍

&#xff08;笔者本人从事过多年芯片开发&#xff0c;一谈起这个话题&#xff0c;眉飞色舞两眼直冒光&#xff01;&#xff01;&#xff09; 处理器芯片是计算系统中的核心组件之一&#xff0c;用于执行各种计算任务和控制系统的操作&#xff1b;只要是电子设备几乎都离不开处理…

python编程不良习惯纠正: 慎用顶层代码

这几天在跑一个开源代码时&#xff0c;发现&#xff0c;通过pdb断点不起作用&#xff0c;经过一番检查&#xff0c;发现代码运行时甚至没有进入main函数,就开始一顿操作. 然后定位到是在执行"import"操作的时候发生了冗余操作. 经过进一步的检查发现&#xff0c;是下…

VS2022编译CMake的工程

开源项目大都是用Make文件组织项目代码编译。对熟悉Window体系&#xff0c;一直用VS套件工作的人&#xff0c;还是有不小的隔阂。 好在有大神们帮助我们解决此类问题&#xff0c;使用CMake工具&#xff0c;可以自动转换工程类型。 1、解压缩代码&#xff0c;找到CMakeList.tx…

D3.js

介绍 概述&#xff1a;D3.js&#xff08;Data-Driven Documents&#xff09;由 Mike Bostock &#xff08;著名的计算机科学家和数据可视化专家&#xff09;创建。是一个用于基于数据的文档操作的JavaScript库。它使用HTML, SVG, 和 CSS 来将数据生动地展现出来。D3.js 的核心…

Python考试复习--day3

1.统计字符串个数 ninput() z0 s0 k0 o0 for i in n:if i.isalpha():zz1elif i.isnumeric():ss1elif i.isspace():k1else:o1 print(字母有{}个,数字有{}个,空格有{}个,其他字符{}个.format(z,s,k,o))2.分类统计字符 ninput() x0 d0 s0 k0 o0 for i in n:if i.islower():x1elif …

程序员创业选搭档很重要

技术人员创业&#xff0c;选对搭档至关重要。 对于想要开展软件项目的技术人员来说&#xff0c;找到一位优秀的技术搭档是极其重要的。 仅仅依靠社会上招聘人员并支付工资的方式&#xff0c;成功的可能性并不高&#xff08;这种方式只适用于已有一定规模的公司进行定向开发&a…

韩愈,文起八代之衰的儒学巨匠

&#x1f4a1; 如果想阅读最新的文章&#xff0c;或者有技术问题需要交流和沟通&#xff0c;可搜索并关注微信公众号“希望睿智”。 韩愈&#xff0c;字退之&#xff0c;生于唐代宗大历三年&#xff08;公元768年&#xff09;&#xff0c;卒于唐穆宗长庆四年&#xff08;公元82…

get()和 load()的区别?

get()和load()是两个不同的函数&#xff0c;它们的主要区别在于它们用于不同的情况。 get()函数&#xff1a;get()函数通常用于从数据库或其他数据源中检索数据。它接受一个参数&#xff0c;通常是一个唯一标识符&#xff0c;用于指定要检索的特定数据。如果找到匹配项&#xf…

wpf自定义按钮样式

在WPF中&#xff0c;自定义按钮样式可以通过创建一个ControlTemplate来实现。以下是一个简单的自定义按钮样式的例子&#xff1a; 首先&#xff0c;在你的WPF项目资源字典中定义按钮的ControlTemplate。 <Window.Resources><ControlTemplate x:Key"CustomButto…

武汉网红餐馆火灾背后的安全警示:可燃气体报警器需定期校准

在餐饮业快速发展的今天&#xff0c;安全问题一直是行业内外关注的重点。 最近&#xff0c;武汉一家网红餐馆在就餐高峰期突发火灾&#xff0c;事件迅速成为公众关注的焦点。这一事故不仅给餐馆带来了重大损失&#xff0c;也引发了对于餐馆安全管理的深思。 尤其是可燃气体报…

基于SqlSugar的开发框架循序渐进介绍(20)-- 在基于UniApp+Vue的移动端实现多条件查询的处理

在做一些常规应用的时候&#xff0c;我们往往需要确定条件的内容&#xff0c;以便在后台进行区分的进行精确查询&#xff0c;在移动端&#xff0c;由于受限于屏幕界面的情况&#xff0c;一般会对多个指定的条件进行模糊的搜索&#xff0c;而这个搜索的处理&#xff0c;也是和前…

关于亚马逊、速卖通、虾皮、Lazada等平台自养号测评IP的重要性

在自养号测评中&#xff0c;IP的纯净度是一个至关重要的问题&#xff0c;它直接关系到账号的安全性和稳定性如果使用了被平台识别为异常或存在风险的IP地址&#xff0c;那么账号可能会面临被封禁的风险。这将对账号的正常使用和测评过程中造成严重影响。而使用纯净的IP地址&…

使用 Django ORM 进行数据库操作

文章目录 创建Django项目和应用定义模型查询数据更新和删除数据总结与进阶聚合和注解跨模型查询原始SQL查询 Django是一个流行的Web应用程序框架&#xff0c;它提供了一个强大且易于使用的对象关系映射&#xff08;ORM&#xff09;工具&#xff0c;用于与数据库进行交互。在本文…

Flutter 中的 DefaultTabController 小部件:全面指南

Flutter 中的 DefaultTabController 小部件&#xff1a;全面指南 在Flutter中&#xff0c;DefaultTabController是一个用于管理Tab控制器的widget&#xff0c;它允许你控制Tab视图的初始索引和动态更新。这个组件在实现具有可滚动标签页的界面时非常有用&#xff0c;例如在设置…

优路教育:为行业发展培养高素质技术技能人才,推进新质生产力发展

职业教育是面向行业、企业培养技术技能人才的特色类型教育。随着技术和市场的加速演进&#xff0c;新技术、新业态出现&#xff0c;相应地迭现出众多新兴职业&#xff0c;也暴露了各新兴领域庞大的人才缺口。其中&#xff0c;高技能人才的高需求&#xff0c;也为职业教育育人标…

Java中的代理模式:灵活地控制对象访问和行为

代理模式是一种结构型设计模式&#xff0c;它允许一个对象&#xff08;代理对象&#xff09;代替另一个对象&#xff08;被代理对象&#xff09;来控制对其的访问。代理对象通常在不改变原始对象的情况下&#xff0c;提供额外的功能或控制访问的方式。在Java编程中&#xff0c;…