LeetCode 146. LRU 缓存机制(LRU Cache)

146. LRU 缓存

📘题目描述

请你设计并实现一个满足  LRU (最近最少使用) 缓存 约束的数据结构。

实现 LRUCache 类:

  • LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
  • int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
  • void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。

函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。

示例:

输入
["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 缓存的核心要求:

  • 访问过的数据必须移到最前面

  • 超出容量时,需要淘汰最久未访问的数据(尾部)

✅数据结构选择:OrderedDict(有序字典)

Python 的 collections.OrderedDict 是标准库中专为 LRU/缓存设计的容器,具备以下特性:

  • 保持插入顺序

  • 可用 move_to_end(key) 将元素移到末尾(表示最近使用)

  • 使用 popitem(last=False) 可以O(1) 删除最久未使用的元素(头部)

✅Python 实现 

from collections import OrderedDict

class LRUCache:

    def __init__(self, capacity: int):
        self.cache = OrderedDict()  # 初始化有序字典
        self.capacity = capacity    # 最大容量

    def get(self, key: int) -> int:
        if key not in self.cache:
            return -1  # 不存在返回 -1
        # 将访问的元素移到末尾(表示最近使用)
        self.cache.move_to_end(key)
        return self.cache[key]

    def put(self, key: int, value: int) -> None:
        if key in self.cache:
            # 移到末尾,准备更新
            self.cache.move_to_end(key)
        self.cache[key] = value
        # 超出容量,弹出最久未使用的元素
        if len(self.cache) > self.capacity:
            self.cache.popitem(last=False)  # FIFO 移除头部元素

⏱️时间与空间复杂度 

操作时间复杂度空间复杂度
getO(1)O(capacity)
putO(1)O(capacity)

🧱易错点总结 

易错点正确做法说明
更新数据未移动位置move_to_end(key) 保证“最近使用”
put 超过容量未淘汰使用 popitem(last=False) 删除头部
get 不存在时返回错误返回 -1

🎯总结

  • 使用 OrderedDict 是 Python 实现 LRU 的最简洁且效率最高的方式。

  • 同时掌握 哈希表 + 双向链表 手动实现 LRU 也是面试时加分项。

  • 本题考查的是缓存机制 + 数据结构的组合使用,属于高频面试题之一。

🔄补充拓展:不允许使用 OrderedDict

如果面试官要求不能使用内置容器,可使用:

  • 哈希表 + 双向链表手动实现

  • 哈希表存储 key → Node 映射

  • 双向链表维护使用顺序:最近使用的节点放在头部,最久未使用的节点放在尾部

🔨 LRU Cache:哈希表 + 双向链表 手动实现版

🎯核心思路

  1. 双向链表维护使用顺序(头:最近使用,尾:最久未使用)

  2. 哈希表实现快速查找 key → 节点(O(1)) 

✅ Python 代码(不依赖任何标准库) 

class Node:
    def __init__(self, key: int, value: int):
        self.key = key
        self.value = value
        self.prev = None
        self.next = None

class LRUCache:

    def __init__(self, capacity: int):
        self.capacity = capacity
        self.cache = {}  # key -> Node
        # 使用两个哨兵节点,head.next 是最常用,tail.prev 是最少用
        self.head = Node(0, 0)
        self.tail = Node(0, 0)
        self.head.next = self.tail
        self.tail.prev = self.head

    # 内部方法:将节点移动到头部(表示最近使用)
    def _move_to_head(self, node: Node):
        self._remove(node)
        self._add(node)

    # 内部方法:添加节点到头部
    def _add(self, node: Node):
        node.prev = self.head
        node.next = self.head.next

        self.head.next.prev = node
        self.head.next = node

    # 内部方法:移除链表中的节点
    def _remove(self, node: Node):
        prev = node.prev
        nxt = node.next
        prev.next = nxt
        nxt.prev = prev

    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 = Node(key, value)
            self.cache[key] = node
            self._add(node)
            if len(self.cache) > self.capacity:
                # 淘汰尾部最久未使用节点
                lru = self.tail.prev
                self._remove(lru)
                del self.cache[lru.key]

🧪使用示例 

lru = LRUCache(2)
lru.put(1, 1)
lru.put(2, 2)
print(lru.get(1))  # 输出 1
lru.put(3, 3)      # 淘汰 key=2
print(lru.get(2))  # 输出 -1
lru.put(4, 4)      # 淘汰 key=1
print(lru.get(1))  # 输出 -1
print(lru.get(3))  # 输出 3
print(lru.get(4))  # 输出 4

时间和空间复杂度与使用OrderedDict方法相同。 

🧱易错点总结

错误点正确方式
删除时未断开前后节点_remove() 正确处理 prev/next
忘记从 cache 删除淘汰节点del self.cache[lru.key]
双向链表头尾更新顺序错误始终更新 prev/next 的双向性
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值