【链表】No. 0146 LRU缓存【中等】👉力扣对应题目指路
希望对你有帮助呀!!💜💜 如有更好理解的思路,欢迎大家留言补充 ~ 一起加油叭 💦
欢迎关注、订阅专栏 【力扣详解】谢谢你的支持!
⭐ 题目描述:请你设计并实现一个满足 LRU (最近最少使用) 缓存约束的数据结构
- 实现 LRUCache 类:
LRUCache(int capacity)
以 正整数 作为容量capacity
初始化 LRU 缓存int get(int key)
- 如果关键字
key
存在于缓存中,则返回关键字的值value
- 否则返回
-1
- 如果关键字
void put(int key, int value)
- 如果关键字
key
已经存在,则变更其数据值value
- 如果不存在,则向缓存中插入该组
key-value
- 如果插入操作导致关键字数量超过
capacity
,则应该 逐出 最久未使用的关键字
- 如果关键字
- 【难点🔥】函数 get 和 put 必须以 O(1) 的平均时间复杂度运行
🔥 思路:哈希表 + 双向链表
- 因为要根据使用的热度频繁调整存储,所以选用双向链表
- 本文采用的顺序为:“最近使用的” 放入尾部
tail
;“长期未用的” 挤到头部head
- 为进一步满足访问
get
的复杂度要求,使用哈希表存储 <key,key 其对应的链表节点>
- 哈希表的使用可以在 O(1) 时间内,通过
key
找到值val
-
示例:
初始化
-put(1,1)
-put(2,2)
-get(1)
-put(3, 3)
- 初始化 -------------------------------------------------------------------------------------------------
- put(1,1) -------------------------------------------------------------------------------------------------
- put(2,2) -------------------------------------------------------------------------------------------------
- get(1) --------------------------------------------------------------------------------------------------
- put(3, 3) --------------------------------------------------------------------------------------------------
,
- 初始化 -------------------------------------------------------------------------------------------------
⭐⭐⭐ 题目准备之双向链表插入、删除元素: 一定要先掌握双向链表插入、删除元素 ❗❗❗ 步骤中用 🍀 重点标记出了~
参考如上思路,给出详细步骤如下:
- 步骤一⭐编写 LRUCache 的
__init__
- 初始化容量
self.capacity
和当前大小self.size
- 初始化双向链表的
self.head, tail
- 不要忘记链接好
self.head, tail
:self.head.next = self.tail;self.tail.last = self.head
- 初始化所需的哈希表
self.dict
- 步骤二⭐编写 LRUCache 的
get
: 如果关键字 key 存在于缓存中,则返回关键字的值 value;否则返回-1
- 通过哈希表的键来进行逻辑判断 key 是否不存在:if not key in self.dict.keys()
- 存在的话,将对应节点移动至链表尾部并返回关键字的值 value
- “短路当前节点”【🍀涉及到两个节点
cur.last
,cur.next
,共更改两条箭头指向】:当前节点的last
和next
链接- “重连当前节点”【🍀涉及到三个节点
tail.last
,cur
,tail
,共更改四条箭头指向】:指向好当前节点的last
和next
- 返回
get
结果- 步骤三⭐编写 LRUCache 的
put
- 情况一:如果关键字
key
已经存在,则变更其数据值value
- 记得操作一次
get
将改被访问的元素移动到tail
- 情况一:如果不存在,则向缓存中插入该组
key-value
- 如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字
- 根据
key-value
建立新链表节点cur
并插入队尾 【🍀涉及到三个节点tail.last
,cur
,tail
,共更改四条箭头指向】
class ListNode:
def __init__(self, val=0, next_node=None, last_node=None):
self.val = val
self.next = next_node
self.last = last_node
class LRUCache:
def __init__(self, capacity: int): # --------------------- step 1
# ---------------------------------------------------- step 1.1
self.capacity = capacity
self.size = 0
# ---------------------------------------------------- step 1.2
self.head = ListNode()
self.tail = ListNode()
self.head.next = self.tail
self.tail.last = self.head
# ---------------------------------------------------- step 1.3
self.dict = {}
def get(self, key: int) -> int: # ------------------------ step 2
if not key in self.dict.keys(): # ------------------- step 2.1
return -1
# ------------------------------------------------- step 2.2.1
cur = self.dict[key]
cur.last.next = cur.next
cur.next.last = cur.last
# ------------------------------------------------- step 2.2.2
cur.last = self.tail.last
cur.next = self.tail
self.tail.last.next = cur
self.tail.last = cur
# ------------------------------------------------- step 2.2.3
return self.dict[key].val[1]
def remove_head(self):
self.head.next = self.head.next.next
self.head.next.last = self.head
def put(self, key: int, value: int) -> None: # ---------- step 3
# --------------------------------------------------- step 3.1
if key in self.dict.keys():
self.dict[key].val = [key, value]
self.get(key)
return
#------------------------------------------------- step 3.2.1
self.size += 1
if self.size > self.capacity:
self.dict.pop(self.head.next.val[0])
self.remove_head()
self.size -= 1
#------------------------------------------------- step 3.2.2
cur = ListNode([key, value], next_node=self.tail, last_node=self.tail.last)
self.dict[cur.val[0]] = cur
self.tail.last.next = cur
self.tail.last = cur
希望对你有帮助呀!!💜💜 如有更好理解的思路,欢迎大家留言补充 ~ 一起加油叭 💦
🔥 LeetCode 热题 HOT 100