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

力扣146LRU缓存

题目链接:146. LRU 缓存 - 力扣(LeetCode)

请你设计并实现一个满足 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 <= 105

  • 最多调用 2 * 105getput

翻译一下:你往桌面上堆书,并且每本书都有自己的编号,你最多能堆capacity本书。get函数的功能就是让你可以直接通过编号找到对应的书籍,找到之后放在书堆的最上面。put函数就是先看看编号为key的书是否存在,如果不存在就往书堆最上面放一本;如果存在就将原本编号为key的书换掉,再放到书堆最上面。如果书堆满了,那么就需要优先将最下面的书移走。

现在put函数与get函数都要用O(1)的时间复杂度实现。

一开始想直接使用vector,先分配capacity的内存,每个元素值为-1(因为题目说value为非负数)。get函数就先直接用下标获取元素,但是问题来了:怎么把get到的书放在最上面?即我们怎么知道最后一个不是-1的元素在哪里?只能通过遍历。那时间复杂度就不是O(1)而是O(n)了。

可见问题在于预分配了空间。

那我们能不能干脆不预分配空间,用一个变量记录当前vector中元素的数量?这样一来整个vector里都是有效元素。但是问题又来了:如果我们把中间的元素移到了上面,那空出来的位置要补上,这就意味着必然要进行移动操作,那时间复杂度肯定不是O(n)了。

可见问题在于删除元素无法补位。

所以,使用vector肯定是不行了,更不用说队列和栈之类的。根据前面两个问题,可以自然而然想到链表。因为在链表中删除元素是O(1)操作,不涉及补位,也没有预分配空间的问题。

确定使用链表之后再分析:

get函数首先就要解决根据key找到value的问题,所以必须要有一个哈希表来维护键值对。其次要将这个key对应的元素移到链表的最前面。也就是说,除了要根据key值找到value的值,还要找到这个key值对应的节点。这也就意味着,哈希表的value值应该是节点指针而不是int,否则就无法进行移动操作。找到对应节点之后要将其移动到最前面,那这就意味着我们需要知道第二个节点的指针,这有点麻烦,因为第二个节点可能会变化。所以我们不妨搞个虚拟头节点,这样一来移动到头部相当于移动到虚拟头节点的后一位。

put函数首先要判断key是否存在,这个看哈希表即可。如果key存在,更改value即可,value为节点指针的好处就在这里。如果value只是int,那就没办法改变链表中节点的值了。然后我们将这个节点移动到链表头部。可以看到移动到链表头部这个操作使用了多次,因此可以将其单独封装成一个函数。如果key不存在,我们就new一个节点,然后把它放到最前面。如果哈希表的size已经比capacity要大了,就把最后一个节点从链表与哈希表中删除。我们也可以把删除操作封装成一个函数。

这时候新问题来了,怎么知道最后一个节点的key?不知道key就没办法将其从哈希表中删除。使用环形链表可以吗?并不行,因为只知道最后一个节点的next是虚拟头节点。所以要使用双向链表。并且,每个节点要存储key/value/next/prev,因为找到尾节点之后还要返回它的key值,这样才能从哈希表中删除。

另外,可以将由key找对应节点的操作封装为一个函数。在这个函数中,如果找得到节点就将其移到头部

struct Node {int key;int value;Node* prev;Node* next;
​Node(int k = 0,int v = 0):key(k),value(v),prev(nullptr),next(nullptr){}
};
​
class LRUCache {
private:int cap;//容量Node* dummy_head;unordered_map<int, Node*>mp;
​//移动到头部void move_to_front(Node* node) {node->prev = dummy_head;node->next = dummy_head->next;//第一个加入的节点就是最后一个节点node->prev->next = node;node->next->prev = node;}
​//删除节点void del_node(Node* node) {node->prev->next = node->next;node->next->prev = node->prev;}
​//获取key对应的节点并将其移动到头部Node* get_node(int key) {auto it = mp.find(key);if (it == mp.end()) {//没有这本书return nullptr;}Node* node = it->second;del_node(node);//要把书抽出来。不然如果只有dummy_head以及另一个节点node1,就会导致node1自己指向自己move_to_front(node);return node;}
​
public:LRUCache(int capacity) {cap = capacity;dummy_head = new Node();dummy_head->next = dummy_head;dummy_head->prev = dummy_head;}
​int get(int key) {Node* node = get_node(key);return node ? node->value : -1;}
​void put(int key, int value) {Node* node = get_node(key);if (node) {//节点存在,更新其value即可node->value = value;return;}//如果节点不存在,就new一个,并放到最上面node = new Node(key, value);mp[key] = node;move_to_front(node);
​//如果超出容量,删掉最后一个节点if (mp.size() > cap) {Node* del = dummy_head->prev;mp.erase(del->key);del_node(del);delete del;}}
};
http://www.dtcms.com/a/569732.html

相关文章:

  • 网站怎么做充值系统下载网站需要写哪些内容
  • 网站有没有做网站地图怎么看vi设计案例ppt
  • 网站页面设计培训班长沙人才招聘网最新招聘2024
  • 虚幻引擎5 GAS开发俯视角RPG游戏 P07-02 授予能力
  • 真实的大模型中,embedding映射的高维矩阵维度和 attention矩阵运算的规模尺寸?
  • 中山网站设计与建设北京网上注册公司
  • ctf show-misc
  • 电子商务网站开发与管理实验报告青岛seo网络优化公司
  • 广告投放网站网站flash代码
  • 【Solidity 从入门到精通】第2章 Solidity 语言概览与环境搭建
  • 前端缓存战争:回车与刷新按钮的终极对决!
  • 做会计题目的网站手机网站推荐大全
  • 【论文精读】AVID:基于扩散模型的任意长度视频修复
  • 电子学会青少年软件编程(C/C++)1级等级考试真题试卷(2025年9月)
  • 解锁跨平台同步的云端去痕仓,擦擦视频去字幕水印,安卓 /iOS/ 网页端实时协作!
  • C++ map和set的实现和封装
  • 免费个人主页网站品牌设计公司50强
  • visual studio C# 如果只提供某个自建dll的命名空间,但是不添加引用,编译会通过吗
  • 手机手机网站制作应用商店软件大全
  • Maya导出abc文件到ue附带材质属性(中文版)
  • 数智管理学(五十五)
  • Oracle HugePages到底该怎么配置?
  • 河源市seo网站设计抖音带运营团队有用吗
  • 2025年11月4日 AI快讯
  • SAP定价过程
  • ArrayList常见面试题二
  • 网站建设中期报告织梦网站怎么做索引地图
  • 关键字匹配高效算法
  • PySide6 Win10记事本从零到一——第八章 查看菜单界面与功能实现
  • Linux之arm SMMUv3 驱动重要宏和函数解析(11)