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

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

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 已经存在,则变更其数据值 value
		if(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.kler.cn/a/452597.html

相关文章:

  • 数据科学与SQL:如何利用Oracle 计算正态分布概率密度?
  • Leecode刷题C语言之字符串及其反转中是否存在同一子字符串
  • Text组件的用法
  • pytest自动化测试数据驱动yaml/excel/csv/json
  • 远程控制macOS一直卡在100%,能连接上了却只显示了壁纸?
  • 1 软件工程——概述
  • 【贪心】力扣3218. 切蛋糕的最小总开销 I
  • 分布式通信,微服务协调组件,zookeeper
  • C++ OpenCV中读取YAML文件的详解:定义、用途与实用示例
  • 函数式编程Lambda表达式
  • PyTorch model.train() 与 model.eval() 的区别及其源码解析:中英双语
  • PostgreSQL 的历史
  • 医疗平板与普通平板对比:优势尽显
  • 嵌入式学习-QT-Day10
  • 下载 AndroidStudio 旧版本方法
  • Max AI prompt1
  • RK356x bsp 5 - 海华AW-CM358SM Wi-Fi/Bt模组调试记录
  • 云手机群控能用来做什么?
  • 【HarmonyOS应用开发——ArkTS语言】购物商城的实现【合集】
  • 12-C语言单向链表
  • git 项目初始化
  • Linux运维常见命令
  • CE第三次作业
  • 挑战一个月基本掌握C++(第十一天)进阶文件,异常处理,动态内存
  • 在算力魔方上运行Genesis:一款颠覆性开源生成式物理引擎!
  • 云途领航:现代应用架构助力企业转型新篇