2021-09-06146. LRU 缓存机制 哈希表
运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制 。
实现 LRUCache 类:
LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。
进阶:你是否可以在 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
实现本题的两种操作,需要用到一个哈希表和一个双向链表。在面试中,面试官一般会期望读者能够自己实现一个简单的双向链表,而不是使用语言自带的、封装好的数据结构。在 Python 语言中,有一种结合了哈希表与双向链表的数据结构 OrderedDict,只需要短短的几行代码就可以完成本题。在 Java 语言中,同样有类似的数据结构 LinkedHashMap。这些做法都不会符合面试官的要求,因此下面只给出使用封装好的数据结构实现的代码,而不多做任何阐述。
class LRUCache(collections.OrderedDict):def __init__(self, capacity: int):super().__init__()self.capacity = capacitydef get(self, key: int) -> int:if key not in self:return -1self.move_to_end(key)return self[key]def put(self, key: int, value: int) -> None:if key in self:self.move_to_end(key)self[key] = valueif len(self) > self.capacity:self.popitem(last=False)
class LRUCache(collections.OrderedDict):def __init__(self,capacity:int):super().__init__()self.capacity = capacitydef get(self,key:int) ->int:if key not in self:return -1self.move_to_end(key)return self[key]def put(self,key:int,value:int)-> None:if key in self:self.move_to_end(key)self[key] = valueif len(self) >self.capacity:self.popitem(last = False)
class LRUCache(collections.OrderedDict):def __init__(self, capacity: int):super().__init__()self.capacity = capacitydef get(self,key:int)-> int:if key not in self:return -1self.move_to_end(key)return self[key]def put(self,key: int, value:int) ->None:if key in self:self.move_to_end(key)self[key]=valueif len(self) >self.capacity:self.popitem(last= False)
class LRUCache(collections.OrderdDict):def __init__(self, capacity:int):super().__init__()self.capacity = capacitydef get(self,key:int) -> int:if key not in self:return -1self.move_to_end(key)return self[key]def put(self,key:int,value:int) -> None:if key in self:self.move_to_end(key)self[key] = valueif len(self) > self.capacity:self.popitem(last = False)
class LRUCache(collections.OrderedDict):def __init__(self, capacity: int):super().__init__()self.capacity = capacitydef get(self, key:int) -> int:if key not in self:return -1self.move_to_end(key)return self[key]def put(self, key: int, value:int) -> None:if key in self:self.move_to_end(key)self[key] =valueif len(self) >self.capacity:self.popitem(last= False)
方法一:哈希表 + 双向链表
算法
LRU 缓存机制可以通过哈希表辅以双向链表实现,我们用一个哈希表和一个双向链表维护所有在缓存中的键值对。
双向链表按照被使用的顺序存储了这些键值对,靠近头部的键值对是最近使用的,而靠近尾部的键值对是最久未使用的。
哈希表即为普通的哈希映射(HashMap),通过缓存数据的键映射到其在双向链表中的位置。
这样以来,我们首先使用哈希表进行定位,找出缓存项在双向链表中的位置,随后将其移动到双向链表的头部,即可在 O(1)O(1) 的时间内完成 get 或者 put 操作。具体的方法如下:
对于 get 操作,首先判断 key 是否存在:
如果 key 不存在,则返回 -1−1;
如果 key 存在,则 key 对应的节点是最近被使用的节点。通过哈希表定位到该节点在双向链表中的位置,并将其移动到双向链表的头部,最后返回该节点的值。
对于 put 操作,首先判断 key 是否存在:
如果 key 不存在,使用 key 和 value 创建一个新的节点,在双向链表的头部添加该节点,并将 key 和该节点添加进哈希表中。然后判断双向链表的节点数是否超出容量,如果超出容量,则删除双向链表的尾部节点,并删除哈希表中对应的项;
如果 key 存在,则与 get 操作类似,先通过哈希表定位,再将对应的节点的值更新为 value,并将该节点移到双向链表的头部。
上述各项操作中,访问哈希表的时间复杂度为 O(1)O(1),在双向链表的头部添加节点、在双向链表的尾部删除节点的复杂度也为 O(1)O(1)。而将一个节点移到双向链表的头部,可以分成「删除该节点」和「在双向链表的头部添加节点」两步操作,都可以在 O(1)O(1) 时间内完成。
小贴士
在双向链表的实现中,使用一个伪头部(dummy head)和伪尾部(dummy tail)标记界限,这样在添加节点和删除节点的时候就不需要检查相邻的节点是否存在。
2021-09-06146. LRU 缓存机制 哈希表相关推荐
- LRU 缓存机制实现:哈希表 + 双向链表
算法详解 LRU 缓存机制可以通过哈希表辅以双向链表实现,我们用一个哈希表和一个双向链表维护所有在缓存中的键值对. 双向链表按照被使用的顺序存储了这些键值对,靠近头部的键值对是最近使用的,而靠近尾部的 ...
- Leetcode 146. LRU缓存机制【哈希表 [哈希表存储每个元素在双向链表中的指针]+双向链表】
文章目录 问题描述 解题报告 实验代码 参考资料 问题描述 运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制.它应该支持以下操作: 获取数据 get 和 写入数据 put . ...
- 实现 LRU 缓存机制
实现 LRU 缓存机制 文章目录 实现 LRU 缓存机制 一.什么是 LRU 算法 二.LRU 算法描述 三.LRU 算法设计 四.代码实现 一.什么是 LRU 算法 LRU 就是一种缓存淘汰策略.( ...
- LRU缓存机制,你想知道的这里都有
概述 LRU是Least Recently Used的缩写,译为最近最少使用.它的理论基础为 "最近使用的数据会在未来一段时期内仍然被使用,已经很久没有使用的数据大概率在未来很长一段时间仍然 ...
- 146. LRU 缓存机制
146. LRU 缓存机制 运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制 . 实现 LRUCache 类: LRUCache(int capacity) 以正整数作为容量 ...
- Java实现 LeetCode 146 LRU缓存机制
146. LRU缓存机制 运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制.它应该支持以下操作: 获取数据 get 和 写入数据 put . 获取数据 get(key) - ...
- LeetCode 146. LRU缓存机制(哈希链表)
文章目录 1. 题目信息 2. 解题 2.1 手动实现list 2.2 使用内置list 1. 题目信息 运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制.它应该支持以下操作 ...
- 【LeetCode笔记】146. LRU缓存机制(Java、双向链表、哈希表)
文章目录 题目描述 思路 & 代码 LinkedHashMap 的写法 题目描述 大名鼎鼎的超高频面试题 太感动,在这道题上花了太多时间了,今天终于补上博客了TvT 思路 & 代码 结 ...
- LeetCode实战:LRU缓存机制
背景 为什么你要加入一个技术团队? 如何加入 LSGO 软件技术团队? 我是如何组织"算法刻意练习活动"的? 为什么要求团队的学生们写技术Blog 题目英文 Design and ...
- 面试高频题: LRU缓存机制实现
题目 运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制.它应该支持以下操作: 获取数据 get 和 写入数据 put . 获取数据 get(key) - 如果密钥 (key ...
最新文章
- 王建然之欲望和理想的对话
- matlab 字符串替换
- 技术干货 | 高性能短链设计与实现
- c# 运算符 ? ??
- Android--使用VideoView播放视频
- 具有NetBeans,嵌入式GlassFish,JPA和MySQL数据源的Arquillian
- java镂空图案,我想打印镂空的菱形
- ubuntu配置×××后,配置走×××的路线
- 语言非递归求解树的高度_算法素颜(11):无死角“盘”它!二分查找树
- 15. 固若金汤:网站的安全架构
- 极客大学架构师训练营 听课总结 - 架构视图,设计文档 -- 第二课
- matlab 图像保存为视频教程,山东大学《数字图像处理(MATLAB)》江铭炎视频教程
- html简历如何转换成pdf,将拉勾的HTML简历转成PDF
- 【CityEngine教程文档】---01 基础教程
- android html footer 固定,footer保持在页面底部的两种方法
- Android 广告图片 全屏图片适配方案的思路
- mapbox-gl加载带环境贴图白模(视频)
- 【历史上的今天】10 月 3 日:网络空间独立宣言之父诞生;3D 打印概念面世;eBay 收购 PayPal
- 如何恢复移动硬盘数据?
- 同一个用户异地登陆踢人操作
热门文章
- ruby开发以太坊智能合约
- 实验:DHCP中继代理
- 基于 React.js + redux + bootstrap 的 RubyChina 示例
- Todoist Chrome:待办事项列表及任务管理
- Error of Git
- [Linux]如何读取Kernel cmdline
- OpenStreetMap/Google/百度/Bing瓦片地图服务(TMS)
- 李华上大学了吗?(I)
- html全屏代码怎么写,JS实现全屏的四种写法
- a4b5笔记本大小对比_【玩码】全新升级MX350独显 小米笔记本Pro 15 2020款为创造力而生...