codeTop01: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) 的平均时间复杂度运行。
设计一个链表,链表的长度固定,将最近访问到的节点放在链表的头部,刚刚插入的节点也放在链表的头部

思路

使用双向链表+哈希表
在这里插入图片描述

为什么要用双向链表+哈希表?

在将某个节点移到链表头的时候,需要知道该节点的前一个节点和后一个节点,使前一个节点的尾指针指向后一个节点的头指针。
使用哈希表是为了检索的效率O(1)

双向链表的数据结构?

class ListNode{int key;int value;ListNode prev,next;//前后指针public ListNode(){}public ListNode(int key,int value){this.key = key;this.value = value;}
}

初始化LRU缓存

public class LRUCache {int capacity;
Map<Integer, ListNode> map;
ListNode head;
ListNode tail;public LRUCache(int capacity) {this.capacity = capacity;map = new HashMap<>();head = new ListNode();tail = new ListNode();head.next = tail;tail.prev = head;}
}

在这里插入图片描述

在初始化map和缓存容量的同时,新建头尾节点,方便后续的操作

获取值

  public int get(int key) {if (map.containsKey(key)){ListNode listNode = map.get(key);//将key对应的节点插入到链表头deleteAndMoveHead(listNode);return listNode.value;}return -1;}private void deleteAndMoveHead(ListNode listNode) {listNode.prev.next = listNode.next;listNode.next.prev= listNode.prev;moveHead(listNode);}private void moveHead(ListNode listNode) {listNode.next = head.next;listNode.next.prev = listNode;head.next = listNode;listNode.prev = head;}

插入值

public void put(int key, int value) {if (map.containsKey(key)){ListNode listNode = map.get(key);listNode.value = value;deleteAndMoveHead(listNode);}else {if (map.size() == capacity){//删除尾节点moveTail();}//将新节点插入到头部ListNode listNode = new ListNode(key, value);moveHead(listNode);map.put(key,listNode);}//删除尾节点private void moveTail() {ListNode lastNode = tail.prev;lastNode.prev.next = tail;tail.prev =  lastNode.prev;head.prev = tail.prev;map.remove(lastNode.key);}

最终代码

package com.dj.mall.algorithm;import java.util.HashMap;
import java.util.Map;public class LRUCache {int capacity;Map<Integer, ListNode> map;ListNode head;ListNode tail;public LRUCache(int capacity) {this.capacity = capacity;map = new HashMap<>();head = new ListNode();tail = new ListNode();head.next = tail;tail.prev = head;}public int get(int key) {if (map.containsKey(key)){ListNode listNode = map.get(key);//将key对应的节点插入到链表头deleteAndMoveHead(listNode);return listNode.value;}return -1;}private void deleteAndMoveHead(ListNode listNode) {listNode.prev.next = listNode.next;listNode.next.prev= listNode.prev;moveHead(listNode);}private void moveHead(ListNode listNode) {listNode.next = head.next;listNode.next.prev = listNode;head.next = listNode;listNode.prev = head;}public void put(int key, int value) {if (map.containsKey(key)){ListNode listNode = map.get(key);listNode.value = value;deleteAndMoveHead(listNode);}else {if (map.size() == capacity){//删除尾节点moveTail();}//将新节点插入到头部ListNode listNode = new ListNode(key, value);moveHead(listNode);map.put(key,listNode);}}//删除尾节点private void moveTail() {ListNode lastNode = tail.prev;lastNode.prev.next = tail;tail.prev =  lastNode.prev;head.prev = tail.prev;map.remove(lastNode.key);}public static void main(String[] args) {LRUCache lRUCache = new LRUCache(2);lRUCache.put(2, 1); // 缓存是 {1=1}lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}System.out.println(lRUCache.get(1)); // 返回 1lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}System.out.println(lRUCache.get(2)); // 返回 -1 (未找到)lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}System.out.println(lRUCache.get(1));//-1System.out.println(lRUCache.get(3));//3System.out.println(lRUCache.get(4));//4}}//自定义双向链表的每一个节点
class ListNode{int key;int value;ListNode prev,next;public ListNode(){}public ListNode(int key,int value){this.key = key;this.value = value;}
}

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

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

相关文章

opencart3 添加速卖通商品脚本

非爬虫&#xff0c;只能把速卖通商品信息拿下来解析插入到自己的项目里。 刚接触opencart3没多久&#xff0c;有一些新项目需要添加商品&#xff0c;每次手动从速卖通复制信息又很慢&#xff0c;就自己写了一个脚本。 思路&#xff1a;速卖通商品详情页有一段数据包含了几乎所…

初识Hive

官网地址为&#xff1a; Design - Apache Hive - Apache Software Foundation 一、架构 先来看下官网给的图&#xff1a; 图上显示了Hive的主要组件及其与Hadoop的交互。Hive的主要组件有&#xff1a; UI&#xff1a; 用户向系统提交查询和其他操作的用户界面。截至2011年&…

c++树状数组(超多例题讲解)适合有相应基础的

树状数组&#xff08;Fenwick Tree&#xff09;是一种用于高效计算前缀和的数据结构&#xff0c;具有较小的内存占用和较快的查询、更新操作。它广泛应用于解决一维数组的区间查询问题。 树状数组的原理基于二进制的思想。假设有一个长度为n的数组A&#xff0c;树状数组就是用…

基于STC12C5A60S2系列1T 8051单片机的TM1638键盘数码管模块的按键扫描、数码管显示按键值、显示按键LED应用

基于STC12C5A60S2系列1T 8051单片机的TM1638键盘数码管模块的按键扫描、数码管显示按键值、显示按键LED应用 STC12C5A60S2系列1T 8051单片机管脚图STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式及配置STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式介绍TM1638键盘…

C# WinForm AndtUI第三方库 Tree控件使用记录

环境搭建 1.在NuGet中搜索AndtUI并下载至C# .NetFramework WinForm项目。 2.添加Tree控件至窗体。 使用方法集合 1.添加节点、子节点 using AntdUI; private void UpdateTreeView() {Tree tvwTestnew Tree();TreeItem rootTreeItem;TreeItem subTreeItem;Dictionary<str…

高级软件开发知识点

流程 算法题简历上项目用到技术、流程、遇到问题HR 准备 常考的题型和回答思路刷100算法题&#xff0c;理解其思想&#xff0c;不要死记最近一家公司所负责的业务和项目&#xff1a; 项目背景、演进之路&#xff0c;有哪个阶段&#xff0c;每个阶段主要做什么项目中技术选型…

【战略前沿】人形机器人制造商Figure获得了OpenAI、Jeff Bezos、Nvidia和其他科技巨头的资助

原文&#xff1a;Humanoid robot-maker Figure gets funding from OpenAI, Jeff Bezos, Nvidia, and other tech giants 作者&#xff1a;ASSOCIATED PRESS ———————————————— Figure成立不到两年&#xff0c;还没有商业产品&#xff0c;但正在说服有影响力的…

STM32 TIM编码器接口

单片机学习&#xff01; 目录 文章目录 前言 一、编码器接口简介 1.1 编码器接口作用 1.2 编码器接口工作流程 1.3 编码器接口资源分布 1.4 编码器接口输入引脚 二、正交编码器 2.1 正交编码器功能 2.2 引脚作用 2.3 如何测量方向 2.4 正交信号优势 2.5 执行逻辑 三、编码器定时…

[DevOps云实践] 彻底删除AWS云资源

[DevOps云实践] 彻底删除AWS云资源 在实际项目当中&#xff0c;我们经常会遇到云设施资源的删除。如果资源删除不干净&#xff0c;时间一长便会被一些莫名其妙的费用所困扰。 本文将把实际项目过程当中&#xff0c;删除资源时候容易遗漏的点讲解给大家。 1. 彻底删除EC2实例 …

[AIGC] Flink中的Max和Reduce操作:区别及使用场景

Apache Flink提供了一系列的操作&#xff0c;用于对流数据进行处理和转换。在这篇文章中&#xff0c;我们将重点关注两种常见的操作&#xff1a;Max和Reduce。虽然这两种操作在表面上看起来类似——都是对数据进行一些形式的聚合&#xff0c;但它们在应用和行为上有一些关键的区…

15-Java责任链模式 ( Chain of Responsibility)

Java责任链模式 摘要实现范例 责任链模式&#xff08;Chain of Responsibility Pattern&#xff09;为请求创建了一个接收者对象的链 责任链模式给予请求的类型&#xff0c;对请求的发送者和接收者进行解耦 责任链模式中通常每个接收者都包含对另一个接收者的引用&#xff0c…

【replace跳转 和push跳转】

"跳转"一词通常用于指示在程序或网页中从一个位置或页面转到另一个位置或页面。 替代"replace跳转": "replace跳转"通常用于在浏览器中替换当前页面的历史记录。如果您想要替代这种行为&#xff0c;您可以考虑使用以下方法&#xff1a; 使用Ja…

头像剪切上传

头像剪切上传 文章说明核心Api示例源码效果展示源码下载 文章说明 本文主要为了学习头像裁剪功能&#xff0c;以及熟悉canvas绘图和转文件的相关操作&#xff0c;参考教程&#xff08;Web渡一前端–图片裁剪上传原理&#xff09; 核心Api 主要就一个在canvas绘图的操作 context…

2.8k star! 用开源免费的edge-tts平替科大讯飞的语音合成服务

edge-tts是github上的一个开源项目&#xff0c;可以免费将文本转为语音&#xff0c;别看它只有2.8k star&#xff0c;替代科大讯飞的收费TTS服务完全没问题&#xff0c;因为这个项目实际是调用的微软edge的在线语音合成服务&#xff0c;支持40多种语言&#xff0c;300多种声音&…

注意力机制(代码实现案例)

学习目标 了解什么是注意力计算规则以及常见的计算规则.了解什么是注意力机制及其作用.掌握注意力机制的实现步骤. 1 注意力机制介绍 1.1 注意力概念 我们观察事物时&#xff0c;之所以能够快速判断一种事物(当然允许判断是错误的), 是因为我们大脑能够很快把注意力放在事物…

NLP_jieba和hanlp词性对照表_6

jieba词性对照表: - a 形容词 - ad 副形词 - ag 形容词性语素 - an 名形词 - b 区别词 - c 连词 - d 副词 - df - dg 副语素 - e 叹词 - f 方位词 - g 语素 - h 前接成分 - i 成语 - j 简称略称 - k 后接成分 - l 习用语 …

深入了解线程池(代码实战)

文章目录 前言一、线程池是什么&#xff1f;二、如何创建线程池1.使用Executors类2.使用ThreadPoolExecutor类手动配置线程池 总结 前言 随着计算机系统的不断发展和进步&#xff0c;我们需要处理更多的并发任务和复杂的操作。而线程池作为一种高效的线程管理机制&#xff0c;…

EdgeX Foundry 安装部署

文章目录 一、概述1.官方文档2.Docker Compose 生成器3.创建 docker-compose 文件 二、安装准备1. 克隆服务器2.安装 Docker3.安装 docker-compose 三、非安全模式部署1.docker-comepse2.启动 EdgeX Foundry3.访问 UI3.1. consul3.2. EdgeX Console EdgeX Foundry # EdgeX Fou…

Android之Handler原理解析与问题分享

一、Handler运行原理剖析 1.关系剖析图 如果把整个Handler交互看做一个工厂&#xff0c;Thread就是动力MessageQueue是履带Looper是转轴Loooper的loop方法就是开关&#xff0c;当调用loop方法时整个工厂开始循环工作&#xff0c;处理来自send和post提交到MessageQueue的消息&a…

SQL执行后台脚本

SQL进程中断实验 我们操作数据库时&#xff0c;经常遇到数据导入等特别耗时的SQL操作&#xff0c;而关闭MySQL客户端或SSH终端&#xff0c;就会立马关闭SQL会话&#xff0c;导致SQL执行中断&#xff0c;如下实验&#xff1a; 在第一个SSH终端执行 # 进入Mysql客户端&#xf…