📜  使用双链表的 LRU Cache 实现(1)

📅  最后修改于: 2023-12-03 15:22:22.905000             🧑  作者: Mango

使用双链表的 LRU Cache 实现

介绍

当创建一个缓存用于保存最近使用的数据时,很多时候使用 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 字段,prevnext 字段用于链接前后节点。
  • LRUCache:LRU Cache 类,实现了 getput 方法以及一些辅助方法。
  • __init__:初始化 LRU Cache。 capacity 表示缓存的最大容量,dict 表示哈希表,headtail 表示链表的哨兵节点,head 表示链表的头节点,tail 表示链表的末尾节点。
  • get:在哈希表中查找 key,如果不存在则返回 -1。否则,将该节点移动到链表的头部,并返回节点的值。
  • put:检查 key 是否已经存在于哈希表中,如果存在,则更新节点的值,并将节点移动到链表的头部。如果不存在,则创建一个新的节点,并将该节点添加到链表的头部。如果容量已满,则删除链表的末尾节点。
  • add_to_head:将节点添加到链表的头部。
  • remove_node:从链表中删除节点。
  • move_to_head:将节点移动到链表的头部。
  • pop_tail:弹出链表的末尾节点。
总结

使用双链表来实现 LRU Cache 算法,不仅可以保证 LRU 缓存算法原理的正确性,还可以提高代码的可读性和可维护性。