【LeetCode 热题100】146:LRU 缓存(详细解析)(Go语言版)

简介: 本文详细解析了力扣 146 题——LRU 缓存机制的实现方法。通过结合哈希表与双向链表,确保 `get` 和 `put` 操作均在 O(1) 时间内完成。哈希表用于快速查找,双向链表记录访问顺序,支持最近使用数据的高效更新与淘汰。代码以 Go 语言实现,结构清晰,涵盖核心操作如节点移动、插入与删除。此题为面试高频考点,适用于数据缓存、页面置换等场景,掌握后可加深对缓存策略的理解。

🚀 力扣热题 146:LRU 缓存机制(超详细讲解)

📌 题目描述

力扣 146. LRU 缓存

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

实现 LRUCache 类:

  • LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存。
  • int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1
  • void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果不存在,则插入该组键值对。当缓存容量达到上限时,它应该在插入新项之前,使最久未使用的键值对作废

🎯 示例

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

💡 解题思路

✅ 要求实现一个支持 O(1) 时间复杂度的缓存机制:

  • get(key)put(key, value) 都必须在常数时间完成。
  • 关键是:快速访问 + 快速淘汰最近最少使用的数据

⛓️ 数据结构设计

我们需要两个结构配合使用:

结构 作用
哈希表(Map) 实现 O(1) 时间内查找键值对
双向链表(Doubly Linked List) 实现 O(1) 时间内插入和删除节点,用于记录访问顺序

🔨 核心操作

  • put()
    • 键已存在:更新值,并将节点移到链表头部。
    • 键不存在:
      • 空间足够:新节点插入头部。
      • 空间不足:移除链表尾部节点(最久未使用),插入新节点。
  • get()
    • 键不存在:返回 -1
    • 键存在:返回对应值,并将节点移到链表头部(最近使用)

💻 Go 语言实现代码

type Node struct {
   
    key, value int
    prev, next *Node
}

type LRUCache struct {
   
    capacity int
    cache    map[int]*Node
    head     *Node // 虚拟头
    tail     *Node // 虚拟尾
}

func Constructor(capacity int) LRUCache {
   
    head := &Node{
   }
    tail := &Node{
   }
    head.next = tail
    tail.prev = head
    return LRUCache{
   
        capacity: capacity,
        cache:    make(map[int]*Node),
        head:     head,
        tail:     tail,
    }
}

func (this *LRUCache) Get(key int) int {
   
    if node, ok := this.cache[key]; ok {
   
        this.moveToHead(node)
        return node.value
    }
    return -1
}

func (this *LRUCache) Put(key int, value int) {
   
    if node, ok := this.cache[key]; ok {
   
        node.value = value
        this.moveToHead(node)
    } else {
   
        if len(this.cache) >= this.capacity {
   
            removed := this.removeTail()
            delete(this.cache, removed.key)
        }
        newNode := &Node{
   key: key, value: value}
        this.cache[key] = newNode
        this.addToHead(newNode)
    }
}

// 将节点移动到头部
func (this *LRUCache) moveToHead(node *Node) {
   
    this.removeNode(node)
    this.addToHead(node)
}

// 从链表中移除节点
func (this *LRUCache) removeNode(node *Node) {
   
    prev := node.prev
    next := node.next
    prev.next = next
    next.prev = prev
}

// 添加节点到头部
func (this *LRUCache) addToHead(node *Node) {
   
    node.prev = this.head
    node.next = this.head.next
    this.head.next.prev = node
    this.head.next = node
}

// 移除尾部节点(返回被移除的节点)
func (this *LRUCache) removeTail() *Node {
   
    node := this.tail.prev
    this.removeNode(node)
    return node
}

⏳ 复杂度分析

操作 时间复杂度 空间复杂度
get() O(1) O(n)
put() O(1) O(n)
  • 所有操作都只涉及 Map 和链表的插入、删除,时间复杂度为常数。
  • 空间复杂度与缓存容量 n 成正比。

🔍 思维拓展

  • 实现 LRU 机制是面试高频考点,特别是系统设计中常用于:
    • 数据缓存
    • 页面置换
    • 网络缓存策略
  • LRU 和 LFU(最不常用)是最常见的缓存淘汰策略。

🎯 总结

点评 内容
✅ 特点 常数时间插入、删除、查询
✅ 关键 Map + 双向链表
✅ 易错点 双向链表指针操作需谨慎
✅ 面试高频 大厂常考,Redis、浏览器缓存中有应用

如果你觉得这篇文章对你有帮助,欢迎 点赞 👍 收藏 ⭐ 评论 💬 关注 💻,我会持续更新更多 Leetcode 热题解析!📌🎯🚀


目录
相关文章
|
4天前
|
Go 开发者 索引
【LeetCode 热题100】路径与祖先:二叉树中的深度追踪技巧(力扣33 / 81/ 153/154)(Go语言版)
本文深入探讨了LeetCode中四道关于「搜索旋转排序数组」的经典题目,涵盖了无重复和有重复元素的情况。通过二分查找的变形应用,文章详细解析了每道题的解题思路和Go语言实现代码。关键点包括判断有序区间、处理重复元素以及如何缩小搜索范围。文章还总结了各题的异同,并推荐了类似题目,帮助读者全面掌握二分查找在旋转数组中的应用。无论是初学者还是有经验的开发者,都能从中获得实用的解题技巧和代码实现方法。
34 14
|
4天前
|
存储 算法 Go
【LeetCode 热题100】17:电话号码的字母组合(详细解析)(Go语言版)
LeetCode 17题解题思路采用回溯算法,通过递归构建所有可能的组合。关键点包括:每位数字对应多个字母,依次尝试;递归构建下一个字符;递归出口为组合长度等于输入数字长度。Go语言实现中,使用map存储数字到字母的映射,通过回溯函数递归生成组合。时间复杂度为O(3^n * 4^m),空间复杂度为O(n)。类似题目包括括号生成、组合、全排列等。掌握回溯法的核心思想,能够解决多种排列组合问题。
31 11
|
2天前
|
Go
【LeetCode 热题100】155:最小栈(详细解析)(Go语言版)
本文详细解析了力扣热题155:最小栈的解题思路与实现方法。题目要求设计一个支持 push、核心思路是使用辅助栈法,通过两个栈(主栈和辅助栈)来维护当前栈中的最小值。具体操作包括:push 时同步更新辅助栈,pop 时检查是否需要弹出辅助栈的栈顶,getMin 时直接返回辅助栈的栈顶。文章还提供了 Go 语言的实现代码,并对复杂度进行了分析。此外,还介绍了单栈 + 差值记录法的进阶思路,并总结了常见易错点,如 pop 操作时忘记同步弹出辅助栈等。
18 6
|
4天前
|
Go 索引
【LeetCode 热题100】739:每日温度(详细解析)(Go语言版)
这篇文章详细解析了 LeetCode 第 739 题“每日温度”,探讨了如何通过单调栈高效解决问题。题目要求根据每日温度数组,计算出等待更高温度的天数。文中推荐使用单调递减栈,时间复杂度为 O(n),优于暴力解法的 O(n²)。通过实例模拟和代码实现(如 Go 语言版本),清晰展示了栈的操作逻辑。此外,还提供了思维拓展及相关题目推荐,帮助深入理解单调栈的应用场景。
31 6
|
8月前
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
117 6
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
9月前
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
98 6
|
9月前
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
203 2
|
6月前
|
机器学习/深度学习 人工智能 自然语言处理
280页PDF,全方位评估OpenAI o1,Leetcode刷题准确率竟这么高
【10月更文挑战第24天】近年来,OpenAI的o1模型在大型语言模型(LLMs)中脱颖而出,展现出卓越的推理能力和知识整合能力。基于Transformer架构,o1模型采用了链式思维和强化学习等先进技术,显著提升了其在编程竞赛、医学影像报告生成、数学问题解决、自然语言推理和芯片设计等领域的表现。本文将全面评估o1模型的性能及其对AI研究和应用的潜在影响。
171 1
|
8月前
|
数据采集 负载均衡 安全
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
本文提供了多个多线程编程问题的解决方案,包括设计有限阻塞队列、多线程网页爬虫、红绿灯路口等,每个问题都给出了至少一种实现方法,涵盖了互斥锁、条件变量、信号量等线程同步机制的使用。
110 3
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
|
9月前
|
索引 Python
【Leetcode刷题Python】从列表list中创建一颗二叉树
本文介绍了如何使用Python递归函数从列表中创建二叉树,其中每个节点的左右子节点索引分别是当前节点索引的2倍加1和2倍加2。
146 7

热门文章

最新文章