剑指 Offer II 031. 最近最少使用缓存


comments: true
edit_url: https://github.com/doocs/leetcode/edit/main/lcof2/%E5%89%91%E6%8C%87%20Offer%20II%20031.%20%E6%9C%80%E8%BF%91%E6%9C%80%E5%B0%91%E4%BD%BF%E7%94%A8%E7%BC%93%E5%AD%98/README.md

剑指 Offer II 031. 最近最少使用缓存

题目描述

运用所掌握的数据结构,设计和实现一个  LRU (Least Recently Used,最近最少使用) 缓存机制 。

实现 LRUCache 类:

  • LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
  • int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1
  • void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。

 

示例:

输入
["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 <= 105
  • 最多调用 2 * 105getput

 

进阶:是否可以在 O(1) 时间复杂度内完成这两种操作?

 

注意:本题与主站 146 题相同:https://leetcode.cn/problems/lru-cache/ 

解法

方法一:哈希表 + 双向链表

我们可以用“哈希表”和“双向链表”实现一个 LRU 缓存。

  • 哈希表:用于存储 key 和对应的节点位置。
    - 双向链表:用于存储节点数据,按照访问时间排序。o(1)

当访问一个节点时,如果节点存在,我们将其从原来的位置删除,并重新插入到链表头部。这样就能保证链表尾部存储的就是最近最久未使用的节点,当节点数量大于缓存最大空间时就淘汰链表尾部的节点。

当插入一个节点时,如果节点存在,我们将其从原来的位置删除,并重新插入到链表头部。如果不存在,我们首先检查缓存是否已满,如果已满,则删除链表尾部的节点,将新的节点插入链表头部。

时间复杂度 O ( 1 ) O(1) O(1),空间复杂度 O ( c a p a c i t y ) O(capacity) O(capacity)

Python3
class Node:def __init__(self,key=0,val=0):self.key=keyself.val=valself.prev=Noneself.next=Noneclass LRUCache:def __init__(self, capacity: int):self.cache={}self.capacity=capacity#重要标记: 方便o(1)插入首尾,达到 (链头:最近 链位:最久)self.head=Node()self.tail=Node()self.head.next=self.tailself.tail.prev=self.headself.len=0def get(self, key: int) -> int:if key not in self.cache:return -1Nd=self.cache[key]self.exist_to_head(Nd)return Nd.valdef put(self, key: int, value: int) -> None:if key in self.cache:Nd=self.cache[key]Nd.val=valueself.exist_to_head(Nd)else:Nd=Node(key,value)self.put_to_head(Nd)self.len+=1self.cache[key]=Ndif self.len>self.capacity: #注意1位置:先增后删的意义!!!Nd=self.del_tail()self.len-=1del self.cache[Nd.key]def put_to_head(self,node):node.next=self.head.nextnode.prev=self.headnode.next.prev=nodeself.head.next=nodedef del_tail(self):del_node=self.tail.prevprv_node=del_node.prevprv_node.next=self.tailself.tail.prev=prv_nodereturn del_nodedef exist_to_head(self,node):#先 delprv_node=node.prevnx_node=node.nextprv_node.next=nx_nodenx_node.prev=prv_node#再 put_to_headself.put_to_head(node)# Your LRUCache object will be instantiated and called as such:
# obj = LRUCache(capacity)
# param_1 = obj.get(key)
# obj.put(key,value)
Java
class Node {int key;int val;Node prev;Node next;Node() {}Node(int key, int val) {this.key = key;this.val = val;}
}class LRUCache {private Map<Integer, Node> cache = new HashMap<>();private Node head = new Node();private Node tail = new Node();private int capacity;private int size;public LRUCache(int capacity) {this.capacity = capacity;head.next = tail;tail.prev = head;}public int get(int key) {if (!cache.containsKey(key)) {return -1;}Node node = cache.get(key);moveToHead(node);return node.val;}public void put(int key, int value) {if (cache.containsKey(key)) {Node node = cache.get(key);node.val = value;moveToHead(node);} else {Node node = new Node(key, value);cache.put(key, node);addToHead(node);++size;if (size > capacity) {node = removeTail();cache.remove(node.key);--size;}}}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 = node;node.next.prev = node;}private Node removeTail() {Node node = tail.prev;removeNode(node);return 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);*/
C++
struct Node {int k;int v;Node* prev;Node* next;Node(): k(0), v(0), prev(nullptr), next(nullptr) {}Node(int key, int val): k(key), v(val), prev(nullptr), next(nullptr) {}
};class LRUCache {
public:LRUCache(int capacity): cap(capacity), size(0) {head = new Node();tail = new Node();head->next = tail;tail->prev = head;}int get(int key) {if (!cache.count(key)) return -1;Node* node = cache[key];moveToHead(node);return node->v;}void put(int key, int value) {if (cache.count(key)) {Node* node = cache[key];node->v = value;moveToHead(node);} else {Node* node = new Node(key, value);cache[key] = node;addToHead(node);++size;if (size > cap) {node = removeTail();cache.erase(node->k);--size;}}}private:unordered_map<int, Node*> cache;Node* head;Node* tail;int cap;int size;void moveToHead(Node* node) {removeNode(node);addToHead(node);}void removeNode(Node* node) {node->prev->next = node->next;node->next->prev = node->prev;}void addToHead(Node* node) {node->next = head->next;node->prev = head;head->next = node;node->next->prev = node;}Node* removeTail() {Node* node = tail->prev;removeNode(node);return 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);*/
Go
type node struct {key, val   intprev, next *node
}type LRUCache struct {capacity   intcache      map[int]*nodehead, tail *node
}func Constructor(capacity int) LRUCache {head := new(node)tail := new(node)head.next = tailtail.prev = headreturn LRUCache{capacity: capacity,cache:    make(map[int]*node, capacity),head:     head,tail:     tail,}
}func (this *LRUCache) Get(key int) int {n, ok := this.cache[key]if !ok {return -1}this.moveToFront(n)return n.val
}func (this *LRUCache) Put(key int, value int) {n, ok := this.cache[key]if ok {n.val = valuethis.moveToFront(n)return}if len(this.cache) == this.capacity {back := this.tail.prevthis.remove(back)delete(this.cache, back.key)}n = &node{key: key, val: value}this.pushFront(n)this.cache[key] = n
}func (this *LRUCache) moveToFront(n *node) {this.remove(n)this.pushFront(n)
}func (this *LRUCache) remove(n *node) {n.prev.next = n.nextn.next.prev = n.prevn.prev = niln.next = nil
}func (this *LRUCache) pushFront(n *node) {n.prev = this.headn.next = this.head.nextthis.head.next.prev = nthis.head.next = n
}
TypeScript
class LRUCache {capacity: number;map: Map<number, number>;constructor(capacity: number) {this.capacity = capacity;this.map = new Map();}get(key: number): number {if (this.map.has(key)) {const val = this.map.get(key)!;this.map.delete(key);this.map.set(key, val);return val;}return -1;}put(key: number, value: number): void {this.map.delete(key);this.map.set(key, value);if (this.map.size > this.capacity) {this.map.delete(this.map.keys().next().value);}}
}/*** Your LRUCache object will be instantiated and called as such:* var obj = new LRUCache(capacity)* var param_1 = obj.get(key)* obj.put(key,value)*/
Rust
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;struct Node {key: i32,value: i32,prev: Option<Rc<RefCell<Node>>>,next: Option<Rc<RefCell<Node>>>,
}impl Node {#[inline]fn new(key: i32, value: i32) -> Self {Self {key,value,prev: None,next: None,}}
}struct LRUCache {capacity: usize,cache: HashMap<i32, Rc<RefCell<Node>>>,head: Option<Rc<RefCell<Node>>>,tail: Option<Rc<RefCell<Node>>>,
}/*** `&self` means the method takes an immutable reference.* If you need a mutable reference, change it to `&mut self` instead.*/
impl LRUCache {fn new(capacity: i32) -> Self {Self {capacity: capacity as usize,cache: HashMap::new(),head: None,tail: None,}}fn get(&mut self, key: i32) -> i32 {match self.cache.get(&key) {Some(node) => {let node = Rc::clone(node);self.remove(&node);self.push_front(&node);let value = node.borrow().value;value}None => -1,}}fn put(&mut self, key: i32, value: i32) {match self.cache.get(&key) {Some(node) => {let node = Rc::clone(node);node.borrow_mut().value = value;self.remove(&node);self.push_front(&node);}None => {let node = Rc::new(RefCell::new(Node::new(key, value)));self.cache.insert(key, Rc::clone(&node));self.push_front(&node);if self.cache.len() > self.capacity {let back_key = self.pop_back().unwrap().borrow().key;self.cache.remove(&back_key);}}};}fn push_front(&mut self, node: &Rc<RefCell<Node>>) {match self.head.take() {Some(head) => {head.borrow_mut().prev = Some(Rc::clone(node));node.borrow_mut().prev = None;node.borrow_mut().next = Some(head);self.head = Some(Rc::clone(node));}None => {self.head = Some(Rc::clone(node));self.tail = Some(Rc::clone(node));}};}fn remove(&mut self, node: &Rc<RefCell<Node>>) {match (node.borrow().prev.as_ref(), node.borrow().next.as_ref()) {(None, None) => {self.head = None;self.tail = None;}(None, Some(next)) => {self.head = Some(Rc::clone(next));next.borrow_mut().prev = None;}(Some(prev), None) => {self.tail = Some(Rc::clone(prev));prev.borrow_mut().next = None;}(Some(prev), Some(next)) => {next.borrow_mut().prev = Some(Rc::clone(prev));prev.borrow_mut().next = Some(Rc::clone(next));}};}fn pop_back(&mut self) -> Option<Rc<RefCell<Node>>> {match self.tail.take() {Some(tail) => {self.remove(&tail);Some(tail)}None => None,}}
}
C#
public class LRUCache {class Node {public Node Prev;public Node Next;public int Key;public int Val;}private Node head = new Node();private Node tail = new Node();private Dictionary<int, Node> cache = new Dictionary<int, Node>();private readonly int capacity;private int size;public LRUCache(int capacity) {this.capacity = capacity;head.Next = tail;tail.Prev = head;}public int Get(int key) {Node node;if (cache.TryGetValue(key, out node)) {moveToHead(node);return node.Val;}return -1;}public void Put(int key, int Val) {Node node;if (cache.TryGetValue(key, out node)) {moveToHead(node);node.Val = Val;} else {node = new Node() { Key = key, Val = Val };cache.Add(key, node);addToHead(node);if (++size > capacity) {node = removeTail();cache.Remove(node.Key);--size;}}}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 = node;node.Next.Prev = node;}private Node removeTail() {Node node = tail.Prev;removeNode(node);return 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,Val);*/
Swift
class Node {var key: Intvar val: Intvar prev: Node?var next: Node?init() {self.key = 0self.val = 0}init(_ key: Int, _ val: Int) {self.key = keyself.val = val}
}class LRUCache {private var cache = [Int: Node]()private let head: Nodeprivate let tail: Nodeprivate let capacity: Intprivate var size: Intinit(_ capacity: Int) {self.capacity = capacityself.size = 0self.head = Node()self.tail = Node()head.next = tailtail.prev = head}func get(_ key: Int) -> Int {guard let node = cache[key] else {return -1}moveToHead(node)return node.val}func put(_ key: Int, _ value: Int) {if let node = cache[key] {node.val = valuemoveToHead(node)} else {let newNode = Node(key, value)cache[key] = newNodeaddToHead(newNode)size += 1if size > capacity {let tail = removeTail()cache.removeValue(forKey: tail.key)size -= 1}}}private func moveToHead(_ node: Node) {removeNode(node)addToHead(node)}private func removeNode(_ node: Node) {node.prev?.next = node.nextnode.next?.prev = node.prev}private func addToHead(_ node: Node) {node.next = head.nextnode.prev = headhead.next?.prev = nodehead.next = node}private func removeTail() -> Node {let node = tail.prev!removeNode(node)return node}
}/*** Your LRUCache object will be instantiated and called as such:* let obj = LRUCache(capacity)* let ret_1: Int = obj.get(key)* obj.put(key, value)*/

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

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

相关文章

Redis 高可用性:如何让你的缓存一直在线,稳定运行?

&#x1f3af; 引言&#xff1a;Redis的高可用性为啥这么重要&#xff1f; 在现代高可用系统中&#xff0c;Redis 是一款不可或缺的分布式缓存与数据库系统。无论是提升访问速度&#xff0c;还是实现数据的高效持久化&#xff0c;Redis 都能轻松搞定。可是&#xff0c;当你把 …

AI 编码 2.0 分析、思考与探索实践:从 Cursor Composer 到 AutoDev Sketch

在周末的公司【AI4SE 效能革命与实践&#xff1a;软件研发的未来已来】直播里&#xff0c;我分享了《AI编码工具 2.0 从 Cursor 到 AutoDev Composer》主题演讲&#xff0c;分享了 AI 编码工具 2.0 的核心、我们的思考、以及我们的 AI 编码工具 2.0 探索实践。 在这篇文章中&am…

Qt Creator + CMake 构建教程

此教程基于: Qt 6.7.4Qt Creator 15.0.1CMake 3.26.4 Qt 6 以下的版本使用 CMake 构建可能会存在一些问题. 目录 新建窗体工程更新翻译添加资源软件部署(Deploy) 此教程描述了如何一步步在 Qt Creator 中使用 CMake 构建应用程序工程. 涉及 新建窗体工程, 更新翻译, 添加资源, …

锂电池保护板测试仪:电池安全的守护者与创新驱动力

在新能源产业蓬勃发展的今天&#xff0c;锂电池以其高能量密度、长循环寿命和环保特性&#xff0c;成为电动汽车、无人机、便携式电子设备等领域不可或缺的能量来源。然而&#xff0c;锂电池的安全性和稳定性一直是行业关注的焦点。为了确保锂电池在各种应用场景下的可靠运行&a…

岳阳市美术馆预约平台(小程序论文源码调试讲解)

第4章 系统设计 一个成功设计的系统在内容上必定是丰富的&#xff0c;在系统外观或系统功能上必定是对用户友好的。所以为了提升系统的价值&#xff0c;吸引更多的访问者访问系统&#xff0c;以及让来访用户可以花费更多时间停留在系统上&#xff0c;则表明该系统设计得比较专…

【Java】I/O 流篇 —— 转换流与序列化流

目录 转换流原理InputStreamReader 转换输入流构造方法代码示例 OutputStreamWriter 转换输出流构造方法代码示例 练习 序列化流序列化流反序列化流**serialVersionUID**基本概念作用使用方式transient 关键字注意事项 转换流 原理 转换流属于字符流&#xff0c;是字符流和字节…

Mac 版 本地部署deepseek ➕ RAGflow 知识库搭建流程分享(附问题解决方法)

安装&#xff1a; 1、首先按照此视频的流程一步一步进行安装&#xff1a;(macos版&#xff09;ragflowdeepseek 私域知识库搭建流程分享_哔哩哔哩_bilibili 2、RAGflow 官网文档指南&#xff1a;https://ragflow.io 3、RAGflow 下载地址&#xff1a;https://github.com/infi…

计算机三级网络技术备考

#subtotal 1Mbps1024kb128KB12.8M/s #1024B1KB 1024KB1MB 1024MB1GB #路由器的5G信号和平常的波长不同&#xff08;5G的穿墙性能差&#xff09; #局域网LAN&#xff08;一公里内——构成集线机、交换机、同轴电缆&#xff09; #城域网MAN&#xff08;几公里到几十公里——光…

IDEA 2024.1 最新永久可用(亲测有效)

今年idea发布了2024.1版本&#xff0c;这个版本带来了一系列令人兴奋的新功能和改进。最引人注目的是集成了更先进的 AI 助手&#xff0c;它现在能够提供更复杂的代码辅助功能&#xff0c;如代码自动补全、智能代码审查等&#xff0c;极大地提升了开发效率。此外&#xff0c;用…

30 分钟从零开始入门 CSS

前言 最近也是在复习&#xff0c;把之前没写的博客补起来&#xff0c;之前给大家介绍了 html&#xff0c;现在是 CSS 咯。 30分钟从零开始入门拿下 HTML_html教程-CSDN博客 一、CSS简介&#xff1a;给网页“化妆”的神器 CSS&#xff08;层叠样式表&#xff09;就像“化妆“&a…

Game Maker 0.11更新:构建社交竞速游戏并增强玩家互动

在这三部分系列中&#xff0c;我们将介绍如何实现Game Maker 0.11中一些最激动人心的新功能。 欢迎来到我们系列文章的第一篇&#xff0c;重点介绍了The Sandbox Game Maker 0.11更新中的新特性。 The Sandbox Game Maker 0.11是一个多功能工具&#xff0c;帮助创作者通过游戏…

软件供应链安全工具链研究系列——RASP自适应威胁免疫平台(上篇)

1.1 基本能力 RASP是一种安全防护技术&#xff0c;运行在程序执行期间&#xff0c;使程序能够自我监控和识别有害的输入和行为。也就是说一个程序如果注入或者引入了RASP技术&#xff0c;那么RASP就和这个程序融为一体&#xff0c;使应用程序具备了自我防护的能力&#xff0c;…

2024信息技术、信息安全、网络安全、数据安全等国家标准合集共125份。

2024信息技术、信息安全、网络安全、数据安全等国家标准合集&#xff0c;共125份。 一、2024信息技术标准&#xff08;54份&#xff09; GB_T 17966-2024 信息技术 微处理器系统 浮点运算.pdf GB_T 17969.8-2024 信息技术 对象标识符登记机构操作规程 第8部分&#xff1a;通用…

HTTP与网络安全

&#x1f345; 点击文末小卡片 &#xff0c;免费获取网络安全全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、HTTPS和HTTP有怎样的区别呢&#xff1f;HTTPS HTTP SSL/TLS&#xff08;SSL或者TLS&#xff09; HTTP&#xff1a;应用层 SSL/TLS&#xff1a;协议中间层 …

ASP.NET Core 8.0学习笔记(二十八)——EFCore反向工程

一、什么是反向工程 1.原则&#xff1a;DBFirst 2.反向工程&#xff1a;根据数据库表来反向生成实体类 3.生成命令&#xff1a;Scaffold-DbContext ‘连接字符串’ 字符串示例&#xff1a; Server.;DatabaseDemo1;Trusted_Connectiontrue; MultipleActiveResultSets true;Tru…

Unity基础——资源导出分享为Unity Package

一.选中要打包的文件夹&#xff0c;右击&#xff0c;点击Exporting package 二.勾选 Include Dependencies&#xff0c;点击Export Include Dependencies&#xff1a;代表是否包含资源依赖的选项 三.选择保存的位置&#xff0c;即可生成Unity package 最终形成文件&#xff1a…

kafka-leader -1问题解决

一. 问题&#xff1a; 在 Kafka 中&#xff0c;leader -1 通常表示分区的领导者副本尚未被选举出来&#xff0c;或者在获取领导者信息时出现了问题。以下是可能导致出现 kafka leader -1 的一些常见原因及相关分析&#xff1a; 1. 副本同步问题&#xff1a; 在 Kafka 集群中&…

【Java企业生态系统的演进】从单体J2EE到云原生微服务

Java企业生态系统的演进&#xff1a;从单体J2EE到云原生微服务 目录标题 Java企业生态系统的演进&#xff1a;从单体J2EE到云原生微服务摘要1. 引言2. 整体框架演进&#xff1a;从原始Java到Spring Cloud2.1 原始Java阶段&#xff08;1995-1999&#xff09;2.2 J2EE阶段&#x…

内容中台的企业CMS架构是什么?

企业CMS模块化架构 现代企业内容管理系统的核心在于模块化架构设计&#xff0c;通过解耦内容生产、存储、发布等环节构建灵活的技术栈。动态/静态发布引擎整合技术使系统既能处理实时更新的产品文档&#xff0c;也能生成高并发的营销落地页&#xff0c;配合版本控制机制确保内…

Binder通信协议

目录 一,整体架构 二,Binder通信协议 三&#xff0c;binder驱动返回协议 四&#xff0c;请求binder驱动协议 一,整体架构 二,Binder通信协议 三&#xff0c;binder驱动返回协议 binder_driver_return_protocol共包含18个命令&#xff0c;分别是&#xff1a; 四&#xff0c…