📅  最后修改于: 2023-12-03 15:22:22.905000             🧑  作者: Mango
当创建一个缓存用于保存最近使用的数据时,很多时候使用 LRU (Least Recently Used,最近最少使用) 缓存算法来决定哪些数据要被清除,以便新的数据可以进入缓存中。
双链表是实现 LRU Cache 非常有用的一种数据结构。LRU Cache 会维护一个双链表,以便于将最近最少使用的条目移到链表的末端。该算法使用哈希表将值与链表节点配对。
当我们想要访问一个新的节点时,我们首先在哈希表中查找该节点是否已经存在于缓存中。如果已经存在,则将该节点移动到链表的头部。如果不存在,则我们需要在链表的头部创建一个新的节点,并将哈希表中的值和新的节点配对。此外,如果链表已经达到缓存的最大容量,则必须将链表的末端节点删除。
下面的代码实现了基于双链表的 LRU Cache 数据结构:
class ListNode:
def __init__(self, key=None, value=None):
self.key = key
self.value = value
self.prev = None
self.next = None
class LRUCache:
def __init__(self, capacity: int):
self.capacity = capacity
self.dict = {}
self.head = ListNode()
self.tail = ListNode()
self.head.next = self.tail
self.tail.prev = self.head
def get(self, key: int) -> int:
if key not in self.dict:
return -1
node = self.dict[key]
self.move_to_head(node)
return node.value
def put(self, key: int, value: int) -> None:
if key in self.dict:
node = self.dict[key]
node.value = value
self.move_to_head(node)
else:
if len(self.dict) == self.capacity:
tail = self.pop_tail()
del self.dict[tail.key]
new_node = ListNode(key, value)
self.dict[key] = new_node
self.add_to_head(new_node)
def add_to_head(self, node):
node.prev = self.head
node.next = self.head.next
self.head.next.prev = node
self.head.next = node
def remove_node(self, node):
node.prev.next = node.next
node.next.prev = node.prev
def move_to_head(self, node):
self.remove_node(node)
self.add_to_head(node)
def pop_tail(self):
tail = self.tail.prev
self.remove_node(tail)
return tail
ListNode
:双链表节点类,包含一个 key
和一个 value
字段,prev
和 next
字段用于链接前后节点。LRUCache
:LRU Cache 类,实现了 get
和 put
方法以及一些辅助方法。__init__
:初始化 LRU Cache。 capacity
表示缓存的最大容量,dict
表示哈希表,head
和 tail
表示链表的哨兵节点,head
表示链表的头节点,tail
表示链表的末尾节点。get
:在哈希表中查找 key
,如果不存在则返回 -1
。否则,将该节点移动到链表的头部,并返回节点的值。put
:检查 key
是否已经存在于哈希表中,如果存在,则更新节点的值,并将节点移动到链表的头部。如果不存在,则创建一个新的节点,并将该节点添加到链表的头部。如果容量已满,则删除链表的末尾节点。add_to_head
:将节点添加到链表的头部。remove_node
:从链表中删除节点。move_to_head
:将节点移动到链表的头部。pop_tail
:弹出链表的末尾节点。使用双链表来实现 LRU Cache 算法,不仅可以保证 LRU 缓存算法原理的正确性,还可以提高代码的可读性和可维护性。