LRU缓存(哈希+双链表)

ops/2024/9/23 0:04:38/

题目描述

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

题解

  • 使用双链表模拟缓存链表头部作为缓存入口(插入节点),用于保存最近刚被访问的页面链表尾部作为缓存出口(删除节点),用于删除最近最久没有被访问的页面
  • 为了便于链表节点的查找,需要定义一个哈希表,用于保存key和对应节点的关系

  • get函数用于模拟cpu对缓存的访问,也就是对内存页的访问。
    • 由LRU算法原理可知,每访问一次缓存,如果能够命中该页面,则说明该页面刚刚被访问过,因此需要将该节点(页面)移动到链表头部
  • put函数用于模拟向缓冲中插入页面。
    • 每次向缓存中插入页面时,都需要首先检查该页面是否在缓存
    • 如果不在则直接将该页面插入到链表头部(因为该页面刚刚被访问过),同时判断此时缓冲中的大小是否已经超过容量,如果超过则需要从缓存中删除该节点(页面)
    • 如果页面在缓存中,则直接将该页面放入链表头部
struct DLinkNode
{int _key,_val;struct DLinkNode* prev,*next;
};class LRUCache {
private:unordered_map<int,DLinkNode*> _mp;DLinkNode* head,*tail;int _size;int _capacity;public:LRUCache(int capacity):_capacity(capacity),_size(0) {head=new DLinkNode(0);tail=new DLinkNode(0);head->next=tail;tail->prev=head;}int get(int key) {auto it=_mp.find(key);if(it==_mp.end()){return -1;}else{moveHead(it->second);return it->second->_val;}}void put(int key, int value) {auto it=_mp.find(key);if(it==_mp.end()){DLinkNode* e=new DLinkNode(key,value);addHead(e);_size++;_mp[key]=e;if(_size>_capacity){DLinkNode* tmp=tail->prev;removeNode(tmp);_mp.erase(tmp->_key);delete tmp;_size--;}}else{it->second->_val=value;moveHead(it->second);}}void removeNode(DLinkNode* node){node->prev->next=node->next;node->next->prev=node->prev;}void addHead(DLinkNode* node){node->prev=head;node->next=head->next;head->next->prev=node;head->next=node;}void moveHead(DLinkNode* node){removeNode(node);addHead(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);*/


http://www.ppmy.cn/ops/18062.html

相关文章

SpanBert学习

SpanBERT: Improving Pre-training by Representing and Predicting Spans 核心点 提出了更好的 Span Mask 方案&#xff0c;也再次展示了随机遮盖连续一段字要比随机遮盖掉分散字好&#xff1b;通过加入 Span Boundary Objective (SBO) 训练目标&#xff0c;增强了 BERT 的性…

一键设置个性手机壁纸:苹果手机怎么设置动态壁纸?

在苹果手机上设置动态壁纸是一种让你的手机屏幕更生动、更有趣的方式。无论是流动的水滴、绚丽的光影还是动态的星空&#xff0c;动态壁纸可以为你的手机带来全新的视觉体验。苹果手机怎么设置动态壁纸&#xff1f;在本文中&#xff0c;我们将介绍苹果手机上如何设置动态壁纸的…

知识图谱:知识的表示方法

知识表示指的是存储在知识图谱中的数据使用何种语言或者何种数据结构进行描述&#xff0c;从而能够使得知识图谱中的知识运算更加快捷高效。知识表示的方式主要可分为三种&#xff0c;一种是以三元组的形式对知识进行表示&#xff0c;一种是以图结构的形式对知识进行表示&#…

streampetr原版网络nuscenes数据pkl文件中的各字段含义

streampetr原版网络nuscenes数据pkl文件中的各字段含义 每帧数据都包含下列的信息 "token": 该帧数据的标识&#xff0c;具有唯一性 "prev": 该帧数据上一帧数据的token&#xff0c;如果没有就为"" "next": 该帧数据下一帧数据的toke…

通过前端js获取指定年周的开始时间与结束时间(以周一为开始时间)

入参格式&#xff1a;年-周 //截取&#xff1a;具体看入参格式 let year2024; let week2; let weekStartDatenew Date(); let weekEndDatenew Date(); // 创建一个Date对象&#xff0c;设置为指定年份的第一周的周日 let date new Date(year, 0, 1); // 年份, 月份(0…

又重新搭了个个人博客

哈喽大家好&#xff0c;我是咸鱼。 前段时间看到一个学弟写了篇用 Hexo 搭建博客的教程&#xff0c;心中沉寂已久的激情重新被点燃起来。&#xff08;以前搞过一个个人网站&#xff0c;但是因为种种原因最后不了了之&#xff09; 于是花了一天时间参考教程搭了个博客网站&…

以太网口硬件知识分享

一、了解网口通信基本原理 实现网络通信实质上是PHY与MAC及RJ45接口实现信号传输。MAC 就是以太网控制器&#xff0c;MAC属于数据链路层&#xff0c;主要负责把数据封装成帧&#xff0c;对帧进行界定实现帧同步。对MAC地址和源MAC地址及逆行相应的处理并对错误帧进行处理。PHY…

jquery html(““)造成内存上涨

在 jQuery 中&#xff0c;使用 html("") 来清空元素的内容是一种常见的做法。然而&#xff0c;如果不慎用&#xff0c;这可能导致内存使用不当上升&#xff0c;尤其是在涉及到大量的 DOM 操作和事件处理器时。问题通常发生在直接或间接创建了大量的 DOM 元素&#xf…