1. LRU缓存机制深度解析
最近在准备系统设计面试时,我重新梳理了LRU缓存这个经典问题。作为缓存淘汰策略中最常见的实现方式,LRU(Least Recently Used)算法几乎出现在所有大型系统的设计讨论中。今天我就结合自己实现LRU缓存的经验,从底层原理到代码实现,带大家彻底搞懂这个面试高频考点。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. LRU缓存的核心思想
2.1 什么是缓存淘汰策略
在计算机系统中,缓存是一种用空间换时间的设计思想。当我们需要频繁访问某些数据时,将这些数据保存在访问速度更快的存储介质中(如内存),可以显著提升系统性能。但缓存空间有限,当缓存已满时,我们需要决定哪些数据应该被保留,哪些可以被淘汰 - 这就是缓存淘汰策略。
2.2 LRU的基本原理
LRU(最近最少使用)策略基于一个直观的假设:最近被访问过的数据,在不久的将来更有可能被再次访问。因此,当缓存空间不足时,它会优先淘汰最长时间未被访问的数据。
举个例子,假设我们有一个容量为3的LRU缓存:
- 依次存入A、B、C后,缓存顺序是C(最新)→B→A(最旧)
- 当访问B时,B被移到最前面:B→C→A
- 存入D时,A被淘汰,顺序变为D→B→C
3. LRU的数据结构设计
3.1 为什么需要两种数据结构
要实现高效的LRU缓存,我们需要满足两个核心操作:
- 快速查找(Get):O(1)时间复杂度
- 快速更新(Put):O(1)时间复杂度
单独使用任何一种数据结构都无法同时满足这两个要求:
- 哈希表:查找O(1),但无法维护访问顺序
- 链表:可以维护顺序,但查找需要O(n)
因此,LRU通常采用哈希表+双向链表的组合结构。
3.2 双向链表的设计细节
双向链表中的每个节点包含:
- key:用于哈希表查找
- value:实际存储的数据
- prev:指向前驱节点
- next:指向后继节点
使用双向链表而非单向链表的关键原因在于:当我们需要将某个节点移动到链表头部时,需要知道它的前驱节点才能完成删除操作。双向链表可以在O(1)时间内完成这一操作。
4. LRU的操作实现详解
4.1 Get操作实现
当调用get(key)时:
- 首先检查key是否存在于哈希表中
- 如果不存在,返回-1(或null)
- 如果存在:
- 通过哈希表定位到对应节点
- 将该节点从链表中当前位置移除
- 将节点插入链表头部(表示最近使用)
- 返回节点的value
4.2 Put操作实现
当调用put(key, value)时:
- 检查key是否已存在:
- 如果存在:
- 更新节点的value
- 将该节点移动到链表头部
- 如果存在:
- 如果key不存在:
- 创建新节点
- 将节点插入链表头部
- 在哈希表中添加key到节点的映射
- 检查缓存容量:
- 如果超过上限,删除链表尾部的节点(最久未使用)
- 同时从哈希表中移除对应的key
5. 完整Python实现与解析
5.1 节点类设计
python复制class DLinkedNode:
def __init__(self, key=0, value=0):
self.k
