LeetCode刷题碎碎念(八):Design
146. LRU Cache
More detail in Hash Table
哈希表查找快,但是数据无固定顺序;链表有顺序之分,插入删除快,但是查找慢。
双向链表 + 哈希表:
为什么需要双向链表?
因为我们需要删除操作。删除一个节点不光要得到该节点本身的指针,也需要操作其前驱节点的指针,而双向链表才能支持直接查找前驱,保证操作的时间复杂度
O
(
1
)
O(1)
O(1)。
class Node():
def __init__(self, key, val):
self.key = key
self.val = val
self.prev = None
self.next = None
class LRUCache:
def __init__(self, capacity: int):
self.cap = capacity
self.cache = {}
self.head = Node(0, 0) # 哨兵节点
self.tail = Node(0, 0) # 尾节点: 用于链表容量超过最大容量是快速定位、删除尾节点
# 初始化双向链表
self.head.next = self.tail
self.tail.prev = self.head
def get(self, key: int) -> int:
if key in self.cache:
node = self.cache[key]
self.remove_node(node)
self.add_node(node)
return node.val
else:
return -1
def put(self, key: int, value: int) -> None:
# 如果有这个节点,那么删除
if key in self.cache:
self.remove_node(self.cache[key])
# 新建这个节点用来put
node = Node(key, value)
self.cache[key] = node # 添加该节点的映射到哈希表
self.add_node(node) # 添加该节点到链表头部
# 超出cache容量,删掉链表尾部节点
if len(self.cache) > self.cap:
last_node = self.tail.prev # 利用尾部哨兵找到尾节点
self.remove_node(last_node) # 从链表中删除
del self.cache[last_node.key] # 从哈希表中删除索引
# 从链表中删除节点
def remove_node(self, node: Node) -> None:
prev = node.prev
nxt = node.next
prev.next = nxt # 将双向节点自身连结,不再和链表连通
nxt.prev = prev
# 添加节点到链表头部
def add_node(self, node: Node) -> None:
nxt = self.head.next # 利用头哨兵取头节点在链表中的索引
self.head.next = node # 加入节点到头部:node和nxt进行双向连接
node.next = nxt
node.prev = self.head
nxt.prev = node