当前位置: 首页 > news >正文

【Leetcode hot 100】146.LRU缓存

问题链接

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 ,则应该 逐出 最久未使用的关键字。

函数 getput 必须以 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 <= 10^5
  • 最多调用 2 * 10^5getput

问题解答

要解决LRU(最近最少使用)缓存问题,我们需要设计一个数据结构,支持在O(1)平均时间复杂度内完成getput操作。核心思路是结合哈希表(快速查找)和双向链表(维护使用顺序)。

解题思路

  1. 数据结构选择

    • 哈希表(HashMap):存储键到双向链表节点的映射,实现O(1)时间复杂度的查找。
    • 双向链表:维护节点的使用顺序,最近使用的节点放在链表头部,最久未使用的节点放在尾部,支持O(1)时间复杂度的插入、删除和移动操作。
    • 虚拟头节点和虚拟尾节点:简化边界处理(如插入第一个节点或删除最后一个节点时无需判断null)。
  2. 核心操作

    • get(key):若键存在,将对应节点移到链表头部(标记为最近使用)并返回值;否则返回-1。
    • put(key, value):若键存在,更新值并将节点移到头部;若键不存在,创建新节点插入头部,若容量超限,删除链表尾部节点(最久未使用)并从哈希表中移除对应键。

代码实现

import java.util.HashMap;
import java.util.Map;class LRUCache {// 双向链表节点static class Node {int key;int value;Node prev;Node next;public Node(int key, int value) {this.key = key;this.value = value;}}private final Map<Integer, Node> cache; // 哈希表:key -> 节点private final Node head; // 虚拟头节点private final Node tail; // 虚拟尾节点private final int capacity; // 缓存容量public LRUCache(int capacity) {this.capacity = capacity;cache = new HashMap<>(capacity);// 初始化虚拟节点并连接head = new Node(-1, -1);tail = new Node(-1, -1);head.next = tail;tail.prev = head;}public int get(int key) {if (!cache.containsKey(key)) {return -1; // 键不存在,返回-1}Node node = cache.get(key);moveToHead(node); // 访问后移到头部(最近使用)return node.value;}public void put(int key, int value) {if (cache.containsKey(key)) {// 键存在,更新值并移到头部Node node = cache.get(key);node.value = value;moveToHead(node);} else {// 键不存在,创建新节点Node newNode = new Node(key, value);cache.put(key, newNode);addToHead(newNode); // 插入头部// 若容量超限,删除尾部节点(最久未使用)if (cache.size() > capacity) {Node tailNode = removeTail();cache.remove(tailNode.key);}}}// 将节点移到头部(先删除再插入头部)private void moveToHead(Node node) {removeNode(node);addToHead(node);}// 从链表中移除节点private void removeNode(Node node) {node.prev.next = node.next;node.next.prev = node.prev;}// 将节点插入到头部(虚拟头节点之后)private void addToHead(Node node) {node.next = head.next;node.prev = head;head.next.prev = node;head.next = node;}// 移除尾部节点(虚拟尾节点之前的节点)并返回private Node removeTail() {Node res = tail.prev;removeNode(res);return res;}
}

复杂度分析

  • 时间复杂度getput操作均为O(1)。哈希表保证了键的查找为O(1),双向链表保证了节点的插入、删除和移动为O(1)。
  • 空间复杂度:O(capacity),最多存储capacity个键值对。

该实现通过哈希表和双向链表的结合,高效满足了LRU缓存的约束,适用于需要频繁访问且对性能要求较高的场景。


文章转载自:

http://cWNxaz0y.hthmx.cn
http://EUzZiAkn.hthmx.cn
http://TlXPfw2L.hthmx.cn
http://ubaJmfnb.hthmx.cn
http://S4dc80YS.hthmx.cn
http://QQ6R5BBy.hthmx.cn
http://hT7gg7t4.hthmx.cn
http://XXcjLyoR.hthmx.cn
http://SuYzXqLT.hthmx.cn
http://lDhTrvjt.hthmx.cn
http://gZpPTeCr.hthmx.cn
http://1OaS5Rkh.hthmx.cn
http://epQ4Yi7H.hthmx.cn
http://sz9OQPnO.hthmx.cn
http://TLo3kDaT.hthmx.cn
http://fW8NUlkO.hthmx.cn
http://JQOhwQMp.hthmx.cn
http://UTH7Wcgr.hthmx.cn
http://gl63xP8O.hthmx.cn
http://7S5KLzTr.hthmx.cn
http://09ZWkK8F.hthmx.cn
http://IWkpPpwq.hthmx.cn
http://lkypO33w.hthmx.cn
http://e9OCEC9L.hthmx.cn
http://DRbDDXtO.hthmx.cn
http://MRvUYwbj.hthmx.cn
http://DGPuz9yH.hthmx.cn
http://ECJt6cUQ.hthmx.cn
http://CsVtO3MM.hthmx.cn
http://RsaxYBql.hthmx.cn
http://www.dtcms.com/a/374504.html

相关文章:

  • Android 图片 OOM 防护机制设计:大图加载、内存复用与多级缓存
  • Kubernetes 实战练习指南
  • 滴滴二面准备(一)
  • 机器人控制器开发(部署——软件打包备份更新)
  • 企业级CI/CD全流程实战指南
  • VMware与cpolar:虚拟机跨网络协作的无缝解决方案
  • 【深度学习计算机视觉】03:目标检测和边界框
  • IP 访问限制选型指南(含实现示例与存储策略)
  • 思瑞浦 3PEAK ASN:高效率低成本多路音频传输方案,车规级音频芯片国产突破
  • c primer plus 第四章复习题和练习题
  • ES+MySQL实时搜索架构实战解析
  • ​人脸表情识别检测数据集​:近4k图像,8类,yolo标注
  • 【智能协同云图库】基于统一接口架构构建多维度分析功能、结合 ECharts 可视化与权限校验实现用户 / 管理员图库统计、通过 SQL 优化与流式处理提升数据
  • Linux使用-MySQL的使用
  • Linux grep 命令使用说明
  • 双引擎驱动!中新赛克AI安全方案入选网安创新大赛优胜榜单
  • Day42 PHP(mysql注入、跨库读取)
  • 开源 C++ QT Widget 开发(十四)多媒体--录音机
  • 小白成长之路-jenkins使用pipline部署
  • Elasticsearch面试精讲 Day 15:索引别名与零停机更新
  • vscode中使用git、githup的基操
  • Elasticsearch面试精讲 Day 12:数据建模与字段类型选择
  • 【Visual Studio 2017 和 2019下载】
  • 领码方案·AI狂潮:3天极速塑造高可用电商核心架构——从需求到上线,用智能驱动架构革新,打造可扩展、可维护、可复用的电商系统新范式
  • SpringCloud gateway配置predicates的匹配规则
  • Win系统下配置PCL库第一步之下载Visual Studio和Qt 5.15.2(超详细)
  • 腾讯云负载均衡增加访问策略后访问失败
  • 【Java EE进阶 --- SpringBoot】Spring DI详解
  • 内存中读写文件:设计原理与C/C++实现
  • 光场显微镜及其在三维生物成像中的应用