剑指 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.mzph.cn/bicheng/72249.shtml

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

相关文章

uniapp 测试 IPA 包安装到测试 iPhone

将uniapp测试IPA包安装到测试iPhone有以下几种方法&#xff1a; 使用Xcode安装 确保计算机上安装了Xcode&#xff0c;并将iOS设备通过数据线连接到计算机。打开Xcode&#xff0c;在菜单栏中选择Window->Devices and Simulators&#xff0c;在设备列表中找到要安装的iPhone…

vcredist_x64 资源文件分享

vcredist_x64 是 Microsoft Visual C Redistributable 的 64 位版本&#xff0c;用于在 64 位 Windows 系统上运行使用 Visual C 开发的应用程序。它包含了运行这些应用程序所需的运行时组件。 vcredist_x64 资源工具网盘下载链接&#xff1a;https://pan.quark.cn/s/ef56f838f…

weaviate 安装与测试

weaviate 安装 前提条件&#xff1a;docker安装完成 步骤&#xff1a; 开启docker 在终端运行命令 docker run -p 8080:8080 -p 50051:50051 cr.weaviate.io/semitechnologies/weaviate:1.29.0 weaviate 测试 python-client安装代码测试 import weaviate client weaviat…

机器学习:监督学习、无监督学习和强化学习

机器学习&#xff08;Machine Learning, ML&#xff09;是人工智能&#xff08;AI&#xff09;的一个分支&#xff0c;它使计算机能够从数据中学习&#xff0c;并在没有明确编程的情况下执行任务。机器学习的核心思想是使用算法分析数据&#xff0c;识别模式&#xff0c;并做出…

自学微信小程序的第六天

DAY6 1、使用录音API首先需要通过wx.getRecorderManager()方法获取到一个RecorderManager实例,该实例是一个全局唯一的录音管理器,用于实现录音功能。 表32:RecorderManager实例的常用方法 方法名称 说明 start() 开始录音 pause() 暂停录音 resume() 继续录音 stop() 停止…

【数据分析】上市公司市场势力数据测算+dofile(1992-2023年)

市场势力通常指的是公司在市场中的相对竞争力和定价能力。具有较强市场势力的公司通常能够控制价格、影响市场规则&#xff0c;并在竞争中占据主导地位。A股公司市场势力数据是对中国资本市场中公司竞争力的深入分析&#xff0c;A股市场中&#xff0c;公司市场势力的强弱不仅影…

Linux三种网络方式

前言 发现运维啥都得会&#xff0c;这周就遇到了网络问题自己无法解决&#xff0c;因此痛定思痛学一下。 参考文献 你管这破玩意叫网络&#xff1f; 桥接模式、NAT模式、仅主机模式&#xff0c;原来是这样工作的 交换机 构成局域网&#xff0c;实现所有设备之间的通信。 …

DeepSeek + Mermaid编辑器——常规绘图

下面这张图出自&#xff1a;由清华大学出品的 《DeepSeek&#xff1a;从入门到精通》。 作为纯文本生成模型&#xff0c;DeepSeek虽不具备多媒体内容生成接口&#xff0c;但其开放式架构允许通过API接口与图像合成引擎、数据可视化工具等第三方系统进行协同工作&#xff0c;最终…

javaweb将上传的图片保存在项目文件webapp下的upload文件夹下

前端HTML表单 (upload.html) 首先&#xff0c;创建一个HTML页面&#xff0c;允许用户选择并上传图片。 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><title>图片上传</title> </head> <…

2025最新Flask学习笔记(对照Django做解析)

前言&#xff1a;如果还没学Django的同学&#xff0c;可以看Django 教程 | 菜鸟教程&#xff0c;也可以忽略下文所提及的Django内容&#xff1b;另外&#xff0c;由于我们接手的项目大多都是前后端分离的项目&#xff0c;所以本文会跳过对模板的介绍&#xff0c;感兴趣的朋友可…

自然语言处理NLP入门 -- 第十一节NLP 实战项目 3: 文本摘要

1. 为啥需要文本摘要&#xff1f; 还记得小时候我们要写“读后感”或“观后感”吗&#xff1f;看完一篇长长的文章、一本书&#xff0c;甚至一部电影后&#xff0c;老师总是要我们用几句话概括主要内容。其实&#xff0c;这就跟文本摘要的核心思路一样——把那些最有价值、最能…

算法day4 dfs搜索2题

一 糖果 我们看这个蓝桥A组真题 首先我们看这个题目说有M种的糖果&#xff0c;K颗一包&#xff0c;N包糖果 第一行就是输入M&#xff0c;K&#xff0c;N的数量 后面就是输入每个糖果在每包里面的种类 然后问我们最少要用几包糖果才可以把所有种类的糖果都吃一遍 如果不可以吃完…

【MySQL】窗口函数详解(概念+练习+实战)

文章目录 前言1. SQL窗口函数 1.1 窗口函数概念1.2 窗口函数语法1.3 常见窗口函数 1.3.1 聚合窗口函数1.3.2 专用窗口函数 1.4 窗口函数性能比较 2. LeetCode 例题 2.1 LeetCode SQL 178&#xff1a;分数排名2.2 LeetCode SQL 184&#xff1a;最高工资2.3 LeetCode SQL 185&am…

【Ai】--- DeepSeek-r1 如何选择适合自己的版本(超详细)

在编程的艺术世界里&#xff0c;代码和灵感需要寻找到最佳的交融点&#xff0c;才能打造出令人为之惊叹的作品。而在这座秋知叶i博客的殿堂里&#xff0c;我们将共同追寻这种完美结合&#xff0c;为未来的世界留下属于我们的独特印记。 【Ai】--- DeepSeek-r1 如何选择适合自己…

植物大战僵尸金铲铲版 v1.1.6(windows+安卓)

游戏简介 《植物大战僵尸金铲铲版》是由“古见xzz”、“对不起贱笑了”、“是怪哉吖”等联合开发的民间魔改版本&#xff0c;融合了原版塔防玩法与《金铲铲之战》的自走棋元素&#xff0c;属于非官方同人作品。 游戏特点 合成升星机制&#xff1a;三个相同低星植物可合成更高…

网络空间安全(6)web应用程序技术

前言 Web应用程序技术是指用于开发和构建基于Web的应用程序的技术和工具&#xff0c;涵盖了前端开发、后端开发、数据库管理、安全性等多个方面的技术。 一、前端开发技术 HTML/CSS/JavaScript&#xff1a;HTML用于构建网页结构&#xff0c;CSS用于进行样式设计&#xff0c;Jav…

零基础学习OpenGL(一)创建一个窗口

基于 ubuntu 系统&#xff0c;设置基础环境。 #!/usr/bin/env bashsudo apt-get update# 安装基础编译软件 sudo apt-get -y install gcc g cmake git# 安装编译 glfw 依赖的软件 sudo apt-get -y install libwayland-dev libx11-dev libxcursor-dev libxi-dev libxinerama-de…

Windows 11 下正确安装 Docker Desktop 到 D 盘的完整教程

文章目录 Windows 11 在 D 盘正确安装 Docker Desktop 的完整教程**前言****准备工作****1. 手动创建 Docker 相关目录**&#xff08;⚠️ **这一步非常重要**&#xff0c;否则会报错&#xff09;**2. 下载 Docker Desktop 安装程序****3. 使用管理员权限打开终端** **安装 Doc…

版图自动化连接算法开发 00001 ------ 直接连接两个给定的坐标点

版图自动化连接算法开发 00001 ------ 直接连接两个给定的坐标点 引言正文定义坐标点的类绘图显示代码直接连接两个坐标点引言 由于人工智能的加速普及,每次手动绘制版图都会觉得特别繁琐,作者本人在想可否搞一个自动化连接器件端口的算法,后期可以根据一些设定的限制进行避…

AIP-156 单例资源

编号156原文链接AIP-156: Singleton resources状态批准创建日期2019-05-12更新日期2024-04-15 API有时需要表示在任意上级资源中&#xff0c;始终只存在一个实例的资源。常见的例子是配置对象。 指南 API 可以 定义 单例资源 。单例资源 必须 始终随上级资源而存在&#xff…