LeetCode 热题 100_LRU 缓存(35_146_中等_C++)(哈希表 + 双向链表)(构造函数声明+初始化列表=进行变量初始化和赋值)

devtools/2025/1/2 19:59:35/

LeetCode 热题 100_LRU 缓存(35_146)

    • 题目描述:
    • 输入输出样例:
    • 题解:
      • 解题思路:
      • 代码实现(思路一(哈希表 + 双向链表)):
      • 部分代码解读

题目描述:

请你设计并实现一个满足 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) 的平均时间复杂度运行。

输入输出样例:

示例 :
输入
[“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 * 105 次 get 和 put

题解:

解题思路:

思路一(哈希表 + 双向链表):
1、通过题目分析,put操作:如果key不在缓存中那我们需要进行结点的插入操作,若插入时缓存已满则先删除最久未访问的结点再插入。这里我们可以想到双向链表,头部存储最近访问结点,尾部存储最久未访问结点。get操作,若存在key则返回结点的value,这里get相当于一个查找,所以我们可以想到哈希表,这样我们就能快速的进行结点的查找和定位。

2、具体思路如下:
① 我们创建一个头结点和一个额外的尾结点来方便结点的插入和删除操作。
② 插入操作(put):我们将刚插入的元素或者最近使用的元素放在链表的头部,则尾部为最长时间未使用的元素。
     若要插入结点key时,之前存在序号为key的结点则移到链表头部。
     若要插入节点key时,之前不存在序号为key的结点,且结点数未满则插入链表头部,若结点数已满则插入后删除尾结点。

③ 获取操作(get):分析到获取我们很快能想到哈希表,因哈希表能让我们快速的进行查找操作,所以上述插入和删除时需要维护一个哈希表。
     若结点不在哈希表中则返回-1。
     若存在哈希表中则返回值,并将结点移动到头部。

力扣官方题解链接(有缓存 get() 和put () 过程图很不错)

3、复杂度分析
① 时间复杂度:对于 put 和 get 都是 O(1)。
② 空间复杂度:O(capacity),因为哈希表和双向链表最多存储 capacity+1 个元素。

代码实现(思路一(哈希表 + 双向链表)):

struct DLinkedNode{int key,value;DLinkedNode* prev;DLinkedNode* next;DLinkedNode():key(0),value(0),prev(nullptr),next(nullptr){}DLinkedNode(int _key,int _value):key(_key),value(_value),prev(nullptr),next(nullptr){} 
}; class LRUCache {private://存储缓存中的节点数 unordered_map<int,DLinkedNode*> cache;//head和tail方便结点的操作 DLinkedNode* head;DLinkedNode* tail;//size是当前缓存中的结点数,capacity是缓存最大的容量 int size;int capacity;public://初始化缓存缓存容量为 _capacity,一开始缓存存入size=0个结点 LRUCache(int _capacity):capacity(_capacity),size(0){head=new DLinkedNode();tail=new DLinkedNode();head->next=tail;tail->prev=head;}//如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 int get(int key){if(!cache.count(key)){return -1;}//若存在key,则将key对应的结点移动到链表首部,先拆出来,再添加到首部 removeNode(cache[key]);addToHead(cache[key]);return cache[key]->value;} void put(int key,int value){//如果关键字 key 已经存在,则变更其数据值 valueif(cache.count(key)){removeNode(cache[key]);addToHead(cache[key]);cache[key]->value=value;//如果不存在,则向缓存中插入该组 key-value}else{DLinkedNode *newNode=new DLinkedNode(key,value);cache[key]=newNode;addToHead(newNode);++size;//如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。if(size>capacity){DLinkedNode *removed=removeTail();cache.erase(removed->key);delete removed;--size;}}}//插入链表头部void addToHead(DLinkedNode *node){node->next=head->next;node->prev=head;head->next->prev=node;head->next=node;}//断开链表尾部(需返回,用于删除哈希表中对应数据)DLinkedNode *removeTail(){DLinkedNode *node=tail->prev;tail->prev=node->prev;node->prev->next=tail;return node; }//断开结点 void removeNode(DLinkedNode* node) {node->prev->next = node->next;node->next->prev = node->prev;}
};

部分代码解读

构造函数声明+初始化列表进行变量初始化和赋值

//下方代码的用法和第一行相同,是构造函数初始化列表,对变量初始化和赋值
DLinkedNode(int _key,int _value):key(_key),value(_value),prev(nullptr),next(nullptr){} 
//构造函数
//初始化 capacity 成员变量 为传递给构造函数的参数 _capacity
//初始化 size 成员变量 为 0,表示缓存初始化时为空。
LRUCache(int _capacity):capacity(_capacity),size(0){}

LeetCode 热题 100_LRU 缓存(35_146)原题链接
欢迎大家和我沟通交流(✿◠‿◠)


http://www.ppmy.cn/devtools/146808.html

相关文章

【每日学点鸿蒙知识】节点析构问题、区分手机和pad、 Navigation路由问题、Tabs组件宽度、如何监听Map

1、HarmonyOS 只调用根节点的dispose,是否其下的子节点都能析构掉还是需要遍历子节点&#xff0c;都执行dispose才能正常析构&#xff1f; 前端持有引用关系的需要dispose&#xff0c;new出来的builderNode和FrameNode也需要dispose。只调用根节点的dispose,无法保证其下的子节…

FFmpeg 的常用API

FFmpeg 的常用API 附录&#xff1a;FFmpeg库介绍 库介绍libavcodec音视频编解码核心库编码 (avcodec_send_frame, avcodec_receive_packet)。解码 (avcodec_send_packet, avcodec_receive_frame)。libavformat提供了音视频流的解析和封装功能&#xff0c;多种多媒体封装格式&…

讲解substr函数

substr JavaScript 中的 substr语法示例注意 PHP 中的 substr语法示例 Python 中的等价方法语法示例 其他语言Java 补充 substr 是编程中用于截取字符串的一个方法或函数&#xff0c;其功能是从一个字符串中提取出一部分子字符串。不同的编程语言中&#xff0c;这个功能的实现方…

WiFi、蓝牙共存,物联网无线通信技术,设备无线连接数据传输应用

WiFi、蓝牙共存 一、简介 什么是共存 共存是指允许多个2.4GHZ**&#xff08;频段范围2400-2483.5MHZ&#xff09;**技术&#xff08;包括WiFi、Zigbee、Thread和蓝牙&#xff09;同时存在而不会发生来自一个无线电的信号干扰相邻无线信号的现象 为什么要用WiFi、蓝牙共存 …

华为浏览器(HuaweiBrowser),简约高效上网更轻松

华为浏览器是一款由华为公司自主研发的网页浏览工具&#xff0c;凭借其独特的设计理念和优质的用户体验&#xff0c;正在吸引越来越多的用户关注。这款基于Chromium技术打造的浏览器不仅继承了Chrome的高性能特质&#xff0c;更融入了华为自身的创新元素&#xff0c;为用户打造…

音视频入门基础:MPEG2-TS专题(22)——FFmpeg源码中,获取TS流的音频信息的实现

音视频入门基础&#xff1a;MPEG2-TS专题系列文章&#xff1a; 音视频入门基础&#xff1a;MPEG2-TS专题&#xff08;1&#xff09;——MPEG2-TS官方文档下载 音视频入门基础&#xff1a;MPEG2-TS专题&#xff08;2&#xff09;——使用FFmpeg命令生成ts文件 音视频入门基础…

Html——10 关键字和描述

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>淘宝网</title><meta name"keywords" content"我要自学网,自学HTML,自学CSS"/><meta name"description" content"要设置…

C++ 设计模式:观察者模式(Observer Pattern)

链接&#xff1a;C 设计模式 链接&#xff1a;C 设计模式 - 模板方法 链接&#xff1a;C 设计模式 - 策略模式 观察者模式&#xff08;Observer Pattern&#xff09;是一种行为设计模式&#xff0c;它定义了一种一对多的依赖关系&#xff0c;让多个观察者对象同时监听某一个主…