LeetCode 146. LRU Cache 题解

LeetCode 146. LRU Cache 题解

题目描述

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

实现 LRUCache 类:

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

示例 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]

解题思路

使用双向链表 + 哈希表实现:

  • 双向链表:按使用顺序排列,最近使用的在头部,最久未使用的在尾部
  • 哈希表:快速查找节点

代码实现

python 复制代码
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):
        self.cache = dict()  # key -> node
        self.head = DLinkedNode()  # 虚拟头节点
        self.tail = DLinkedNode()  # 虚拟尾节点
        self.head.next = self.tail
        self.tail.prev = self.head
        self.capacity = capacity
        self.size = 0
    
    def get(self, key):
        if key not in self.cache:
            return -1
        # 将访问的节点移到头部
        node = self.cache[key]
        self.moveToHead(node)
        return node.value
    
    def put(self, key, value):
        if key in self.cache:
            # 更新值并移到头部
            node = self.cache[key]
            node.value = value
            self.moveToHead(node)
        else:
            # 创建新节点
            node = DLinkedNode(key, value)
            self.cache[key] = node
            self.addToHead(node)
            self.size += 1
            
            # 超出容量,删除尾部节点
            if self.size > self.capacity:
                removed = self.removeTail()
                del self.cache[removed.key]
                self.size -= 1
    
    def addToHead(self, node):
        node.prev = self.head
        node.next = self.head.next
        self.head.next.prev = node
        self.head.next = node
    
    def removeNode(self, node):
        node.prev.next = node.next
        node.next.prev = node.prev
    
    def moveToHead(self, node):
        self.removeNode(node)
        self.addToHead(node)
    
    def removeTail(self):
        node = self.tail.prev
        self.removeNode(node)
        return node

复杂度分析

  • 时间复杂度:get 和 put 操作都是 O(1)
  • 空间复杂度:O(capacity)

测试案例

python 复制代码
# 测试案例 1
lru = LRUCache(2)
lru.put(1, 1)
lru.put(2, 2)
assert lru.get(1) == 1  # 返回 1
lru.put(3, 3)  # 驱逐 2
assert lru.get(2) == -1  # 返回 -1
lru.put(4, 4)  # 驱逐 1
assert lru.get(1) == -1  # 返回 -1
assert lru.get(3) == 3  # 返回 3
assert lru.get(4) == 4  # 返回 4

总结

本题是 LRU 缓存的经典实现。

关键点:

  • 双向链表维护使用顺序
  • 哈希表实现 O(1) 查找
  • 虚拟头尾节点简化操作
  • 移动节点到头部表示最近使用
  • 删除尾部节点表示逐出最久未使用
相关推荐
企业数字化笔记3 小时前
视频人体姿态怎么做三维可视化?2D关键点、深度局限与技术选型
人工智能·算法·音视频
?abc!4 小时前
(数组)581. 最短无序连续子数组
数据结构·算法·leetcode
小郎君6586 小时前
MPTT树复选框的半选状态:一棵百万节点树从递归到计数器,再到区间覆盖
算法
酌沧6 小时前
MemT让记忆成为模型内生能力
jvm·人工智能·算法
hold?fish:palm7 小时前
40 二叉树的直径
c++·leetcode
Shimeri7 小时前
越卷越像:我用遗传算法做了个"内卷"实验
算法·three.js·数据可视化·遗传算法·进化算法
Evand J8 小时前
【PMSM例程1】永磁同步电机(PMSM)转速与转子电角度观测算法|基于扩展卡尔曼滤波(EKF)的传感器状态估计与误差。附下载链接
算法·电机·1024程序员节·卡尔曼滤波
guo_wen_qiang8 小时前
二叉树的层序遍历-使用双端队列Deque
算法·leetcode·1024程序员节
爱读书的小胖8 小时前
LeetCode 55 跳跃游戏 C 语言解题分析
c语言·数据结构·算法·leetcode·1024程序员节
CCblandy8 小时前
前缀缓存命中率不靠引擎靠排列:本机仿真 3 轮,易变字段放开头 98.0%→0.0%
人工智能·python·算法·自然语言处理·性能优化