LeetCode中LRU 缓存机制的示例分析

发布时间:2021-12-15 14:32:44 作者:小新
来源:亿速云 阅读:203

LeetCode中LRU缓存机制的示例分析

引言

在计算机科学中,缓存是一种用于存储临时数据的技术,旨在提高数据访问速度。LRU(Least Recently Used,最近最少使用)是一种常见的缓存淘汰策略,它根据数据的历史访问记录来决定哪些数据应该被保留,哪些数据应该被淘汰。本文将详细分析LeetCode中LRU缓存机制的实现,并通过示例代码进行讲解。

LRU缓存机制的基本概念

什么是LRU缓存?

LRU缓存是一种基于时间局部性原理的缓存策略。它假设最近被访问过的数据在未来被再次访问的概率较高,因此会优先保留这些数据。当缓存空间不足时,LRU缓存会淘汰最久未被访问的数据。

LRU缓存的工作原理

LRU缓存通常使用一个哈希表和一个双向链表来实现。哈希表用于快速查找缓存中的数据,而双向链表用于维护数据的访问顺序。具体来说:

  1. 哈希表:存储键值对,键是数据的标识,值是对应链表节点的指针。
  2. 双向链表:链表中的每个节点存储一个键值对,链表的头部表示最近访问的数据,尾部表示最久未被访问的数据。

当访问一个数据时,LRU缓存会执行以下操作:

  1. 如果数据在缓存中(即哈希表中存在该键),则将对应的链表节点移动到链表头部。
  2. 如果数据不在缓存中,则将数据插入到链表头部,并更新哈希表。如果缓存已满,则淘汰链表尾部的数据。

LeetCode中的LRU缓存问题

LeetCode上有一道经典的LRU缓存问题,题目编号为146。题目要求实现一个LRU缓存类,支持以下操作:

示例

输入:
["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

LRU缓存的实现

数据结构选择

为了实现LRU缓存,我们需要选择合适的数据结构。如前所述,哈希表和双向链表是常用的组合。

代码实现

以下是使用Python实现的LRU缓存类:

class DLinkedNode:
    def __init__(self, key=0, value=0):
        self.key = key
        self.value = value
        self.prev = None
        self.next = None

class LRUCache:
    def __init__(self, capacity: int):
        self.cache = {}
        self.capacity = capacity
        self.size = 0
        self.head = DLinkedNode()
        self.tail = DLinkedNode()
        self.head.next = self.tail
        self.tail.prev = self.head

    def get(self, key: int) -> int:
        if key not in self.cache:
            return -1
        node = self.cache[key]
        self.move_to_head(node)
        return node.value

    def put(self, key: int, value: int) -> None:
        if key in self.cache:
            node = self.cache[key]
            node.value = value
            self.move_to_head(node)
        else:
            node = DLinkedNode(key, value)
            self.cache[key] = node
            self.add_to_head(node)
            self.size += 1
            if self.size > self.capacity:
                removed = self.remove_tail()
                del self.cache[removed.key]
                self.size -= 1

    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 remove_tail(self):
        node = self.tail.prev
        self.remove_node(node)
        return node

代码解析

  1. DLinkedNode类:定义了双向链表的节点结构,每个节点包含键、值、前驱指针和后继指针。
  2. LRUCache类
    • __init__方法:初始化缓存,设置容量,创建哈希表和双向链表的头尾节点。
    • get方法:如果键存在于缓存中,则将对应的节点移动到链表头部,并返回节点的值;否则返回-1。
    • put方法:如果键已经存在,则更新节点的值并将其移动到链表头部;如果键不存在,则创建新节点并插入到链表头部,同时更新哈希表。如果缓存已满,则删除链表尾部的节点,并从哈希表中移除对应的键。
    • add_to_head方法:将节点插入到链表头部。
    • remove_node方法:从链表中移除指定节点。
    • move_to_head方法:将指定节点移动到链表头部。
    • remove_tail方法:移除链表尾部的节点,并返回该节点。

示例分析

让我们通过一个具体的示例来分析LRU缓存的执行过程。

示例输入

["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]

执行过程

  1. 初始化缓存LRUCache(2),缓存容量为2。
  2. 插入键值对put(1, 1),缓存为{1=1}
  3. 插入键值对put(2, 2),缓存为{1=1, 2=2}
  4. 获取键值get(1),返回1,并将键1移动到链表头部,缓存为{2=2, 1=1}
  5. 插入键值对put(3, 3),缓存已满,淘汰键2,缓存为{1=1, 3=3}
  6. 获取键值get(2),返回-1,因为键2已被淘汰。
  7. 插入键值对put(4, 4),缓存已满,淘汰键1,缓存为{3=3, 4=4}
  8. 获取键值get(1),返回-1,因为键1已被淘汰。
  9. 获取键值get(3),返回3,并将键3移动到链表头部,缓存为{4=4, 3=3}
  10. 获取键值get(4),返回4,并将键4移动到链表头部,缓存为{3=3, 4=4}

输出结果

[null, null, null, 1, null, -1, null, -1, 3, 4]

总结

LRU缓存机制是一种高效的缓存淘汰策略,广泛应用于各种系统中。通过哈希表和双向链表的结合,我们可以实现一个时间复杂度为O(1)的LRU缓存。本文通过LeetCode中的LRU缓存问题,详细分析了LRU缓存的实现过程,并通过示例代码展示了其工作原理。希望本文能帮助读者更好地理解LRU缓存机制,并在实际开发中灵活运用。

推荐阅读:
  1. Django中缓存机制的示例分析
  2. Hibernate中缓存机制的示例分析

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

leetcode

上一篇:leetcode交换字符串中的元素是什么

下一篇:leetcode怎么解决K 个不同整数的子数组

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》