LeetCode 146通关:LRU缓存机制手撕代码从原理到实战

admin 2026-02-10 阅读:21 评论:0
在Java后端大厂面试中,**LeetCode 146 LRU缓存机制手撕代码**是当之无愧的“高频天花板”题型——据鳄鱼java社区2026年面试数据统计,82%的一线互联网公司(阿里、字节、腾讯、美团)会将其作为算法手撕环节的必考题。掌...

在Java后端大厂面试中,**LeetCode 146 LRU缓存机制手撕代码**是当之无愧的“高频天花板”题型——据鳄鱼java社区2026年面试数据统计,82%的一线互联网公司(阿里、字节、腾讯、美团)会将其作为算法手撕环节的必考题。掌握这道题不仅能理解“最近最少使用”的缓存淘汰逻辑,更能体现你对双向链表、哈希表等核心数据结构的灵活运用能力,直接决定面试是否能进入后续的技术面环节。

一、LRU缓存机制核心原理:为什么需要双向链表+哈希表?

LeetCode 146通关:LRU缓存机制手撕代码从原理到实战

LRU(Least Recently Used,最近最少使用)缓存的核心逻辑是:当缓存容量达到上限时,自动淘汰最近最少使用的缓存条目,同时保证数据的访问和插入操作时间复杂度为O(1)。要实现这一点,单一数据结构无法满足需求:

1. **哈希表的优势与局限**:哈希表可以在O(1)时间内查找缓存条目,但无法维护数据的访问顺序,无法快速找到“最近最少使用”的条目; 2. **双向链表的优势与局限**:双向链表可以在O(1)时间内插入、删除节点,轻松维护数据的访问顺序(最近使用的移到头部,最少使用的留在尾部),但无法快速查找目标节点。

因此,LRU缓存的经典实现方案是**双向链表+哈希表**的组合:双向链表维护数据的访问顺序,哈希表存储“key-节点”的映射,实现查找和修改的O(1)时间复杂度。这也是LeetCode 146题目要求的核心考点——手撕这种组合结构的实现逻辑。

二、LeetCode 146 LRU缓存机制手撕代码:两种实现方式

针对LeetCode 146的要求(实现get和put方法,时间复杂度O(1)),鳄鱼java社区的面试训练营通常会教授两种手撕方式:一种是利用Java自带的LinkedHashMap快速实现,适合面试快速作答;另一种是手动实现双向链表+哈希表,深入体现数据结构能力,是面试加分项。

1. 快速实现:基于LinkedHashMap的极简写法

Java的LinkedHashMap本身就支持“按访问顺序排序”的特性,底层正是双向链表+哈希表的结构,因此只需重写removeEldestEntry方法即可实现LRU逻辑:

 
class LRUCache extends LinkedHashMap { 
    private int capacity; 
public LRUCache(int capacity) { 
    // 初始容量,负载因子,accessOrder=true按访问顺序排序 
    super(capacity, 0.75f, true); 
    this.capacity = capacity; 
} 

public int get(int key) { 
    return super.getOrDefault(key, -1); 
} 

public void put(int key, int value) { 
    super.put(key, value); 
} 

// 当缓存大小超过容量时,移除最久未使用的条目 
@Override 
protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) { 
    return size() > capacity; 
} 

}

这种写法仅需20余行代码,面试中若时间紧张可快速写出,但需注意:面试官大概率会追问LinkedHashMap的底层原理,若答不上来会减分——这也是手动实现的必要性所在。

2. 进阶实现:手动构建双向链表+哈希表

手动实现是LeetCode 146 LRU缓存机制手撕代码的核心考察点,需要自定义双向链表节点、LRUCache类,并实现get和put方法的核心逻辑:

步骤1:定义双向链表节点

 
class Node { 
    int key; 
    int value; 
    Node prev; 
    Node next; 
public Node() {} 
public Node(int key, int value) { 
    this.key = key; 
    this.value = value; 
} 

}

节点中同时存储key和value的原因:当缓存满了需要移除尾部节点时,需要通过节点的key去哈希表中删除对应的条目,这是很多面试者容易忽略的坑点。

步骤2:实现LRUCache核心逻辑

 
class LRUCache { 
    private Map cache; // 哈希表:key -> Node 
    private int capacity; // 缓存容量 
    private Node head, tail; // 虚拟头、尾节点,简化边界处理 
public LRUCache(int capacity) { 
    this.capacity = capacity; 
    cache = new HashMap<>(); 
    // 初始化虚拟头、尾节点,避免空指针 
    head = new Node(); 
    tail = new Node(); 
    head.next = tail; 
    tail.prev = head; 
} 

public int get(int key) { 
    Node node = cache.get(key); 
    if (node == null) { 
        return -1; // 缓存未命中,返回-1 
    } 
    // 访问后移到头部,表示最近使用 
    moveToHead(node); 
    return node.value; 
} 

public void put(int key, int value) { 
    Node node = cache.get(key); 
    if (node == null) { 
        // 缓存未命中,创建新节点 
        Node newNode = new Node(key, value); 
        cache.put(key, newNode); 
        // 添加到头部 
        addToHead(newNode); 
        // 判断容量是否超标 
        if (cache.size() > capacity) { 
            // 移除尾部节点(最少使用) 
            Node removed = removeTail(); 
            // 同时从哈希表中删除 
            cache.remove(removed.key); 
        } 
    } else { 
        // 缓存命中,更新value并移到头部 
        node.value = value; 
        moveToHead(node); 
    } 
} 

// 辅助方法:添加节点到头部 
private void addToHead(Node node) { 
    node.prev = head; 
    node.next = head.next; 
    head.next.prev = node; 
    head.next = node; 
} 

// 辅助方法:移除节点 
private void removeNode(Node node) { 
    node.prev.next = node.next; 
    node.next.prev = node.prev; 
} 

// 辅助方法:移到头部(先移除再添加) 
private void moveToHead(Node node) { 
    removeNode(node); 
    addToHead(node); 
} 

// 辅助方法:移除尾部节点 
private Node removeTail() { 
    Node res = tail.prev; 
    removeNode(res); 
    return res; 
} 

}

手动实现需要重点掌握虚拟头/尾节点的作用(避免处理空指针的边界情况)、put方法中缓存命中/未命中的逻辑、以及辅助方法的复用性——这些细节能体现你的工程化思维,是面试加分的关键。

三、LeetCode 146面试坑点:90%的开发者容易踩这些雷

在LeetCode 146 LRU缓存机制手撕代码的面试中,鳄鱼java社区统计了三大高频坑点:

1. **节点仅存储value,未存储key**:当缓存满了要删除尾部节点时,无法通过节点找到对应的key去哈希表中删除条目,导致哈希表出现“脏数据”; 2. **未使用虚拟头/尾节点**:处理空链表、单节点链表时容易出现空指针异常,比如移除头部节点时需要判断前驱是否为空; 3. **put方法未处理缓存命中的情况**:仅处理了缓存未命中的插入逻辑,忘记更新已存在节点的value并移到头部,导致缓存逻辑错误。

比如鳄鱼java社区的一位学员,第一次面试时因节点只存value,导致删除尾部节点后哈希表无法同步删除,被面试官指出后慌乱修改,最终面试失败;后来在训练营中针对性练习,第二次面试时完美避坑,成功拿到字节跳动的offer。

四、实战演练:用LeetCode测试用例验证代码

实现代码后,需要用LeetCode的官方测试用例验证逻辑是否正确:

 
输入: 
["LRUCache","put","put","get","put","get","put","get","get","get"] 
[[2],[1,1],[2,2],[1],[3,3],[2],[4,4],[1],[3],[4]] 
预期输出:
版权声明

本文仅代表作者观点,不代表百度立场。
本文系作者授权百度百家发表,未经许可,不得转载。

分享:

扫一扫在手机阅读、分享本文

热门文章
  • 多线程破局:KeyDB如何重塑Redis性能天花板?

    多线程破局:KeyDB如何重塑Redis性能天花板?
    在Redis以其卓越的性能和丰富的数据结构统治内存数据存储领域十余年后,其单线程事件循环模型在多核CPU成为标配的今天,逐渐显露出性能扩展的“阿喀琉斯之踵”。正是在此背景下,KeyDB多线程Redis替代方案现状成为了一个极具探讨价值的技术议题。深入剖析这一现状,其核心价值在于为面临性能瓶颈、寻求更高吞吐量与更低延迟的开发者与架构师,提供一个经过生产验证的、完全兼容Redis协议的多线程解决方案的全面评估。这不仅是关于一个“分支”项目的介绍,更是对“Redis单线程哲学”与“...
  • 拆解数据洪流:ShardingSphere分库分表实战全解析

    拆解数据洪流:ShardingSphere分库分表实战全解析
    拆解数据洪流:ShardingSphere分库分表实战全解析 当单表数据量突破千万、数据库连接成为瓶颈时,分库分表从可选项变为必选项。然而,如何在不重写业务逻辑的前提下,平滑、透明地实现数据水平拆分,是架构升级的核心挑战。一次完整的MySQL分库分表ShardingSphere实战案例,其核心价值在于掌握如何通过成熟的中间件生态,将复杂的分布式数据路由、事务管理和SQL改写等难题封装化,使开发人员能像操作单库单表一样处理海量数据,从而在不影响业务快速迭代的前提下,实现数据库能...
  • 提升可读性还是制造混乱?深度解析Java var的正确使用场景

    提升可读性还是制造混乱?深度解析Java var的正确使用场景
    自JDK 10引入以来,var关键字无疑是最具争议又最受开发者欢迎的语法特性之一。它允许编译器根据初始化表达式推断局部变量的类型,从而省略显式的类型声明。Java Var局部变量类型推断使用场景的探讨,其核心价值远不止于“少打几个字”,而是如何在减少代码冗余与维持代码清晰度之间找到最佳平衡点。理解其设计哲学和最佳实践,是避免滥用、真正发挥其提升开发效率和代码可读性作用的关键。本文将系统性地剖析var的适用边界、潜在陷阱及团队规范,为你提供一份清晰的“作战地图”。 一、var的...
  • ConcurrentHashMap线程安全实现原理:从1.7到1.8的进化与实战指南

    ConcurrentHashMap线程安全实现原理:从1.7到1.8的进化与实战指南
    在Java后端高并发场景中,线程安全的Map容器是保障数据一致性的核心组件。Hashtable因全表锁导致性能极低,Collections.synchronizedMap仅对HashMap做了简单的同步包装,无法满足万级以上并发需求。【ConcurrentHashMap线程安全实现原理】的核心价值,就在于它通过不同版本的锁机制优化,在保证线程安全的同时实现了极高的并发性能——据鳄鱼java社区2026年性能测试数据,10000并发下ConcurrentHashMap的QPS是...
  • 2026重庆房地产税最新政策解读:起征点31528元/㎡+免税面积180㎡,影响哪些购房者?

    2026重庆房地产税最新政策解读:起征点31528元/㎡+免税面积180㎡,影响哪些购房者?
    2026年重庆房地产税政策迎来新一轮调整,精准把握政策细节对购房者、多套房业主及投资者至关重要。重庆 2026 房地产税最新政策解读的核心价值在于:清晰拆解征收范围、税率标准、免税规则等关键变化,通过具体案例计算纳税金额,帮助市民判断自身税负,提前规划房产配置。据鳄鱼java房产数据平台统计,2026年重庆房产税起征点较2025年上调8.2%,政策调整后约65%的存量住房可享受免税或低税率优惠,而未及时了解政策的业主可能面临多缴税费风险。本文结合重庆市住建委2026年1月最新...
标签列表