在 Python 技术面试中,LRU 缓存机制(Least Recently Used Cache) 是一道非常高频的算法题。
它不仅考察候选人对 数据结构(哈希表 + 双向链表) 的理解,也检验对 时间复杂度优化思维 的掌握。
本文将从面试官角度出发,带你一步步拆解 LRU 的原理与实现。
一、什么是 LRU 缓存机制?
LRU,全称 Least Recently Used,即 "最近最少使用"。
它的核心思想是:
当缓存容量满时,优先淘汰 最近最少被使用 的数据。
形象理解:
你有一个小抽屉,只能放 3 个文件。
当放入第 4 个文件时,你要扔掉那个 最久没碰过 的文件,这就是 LRU。
二、为什么要用 LRU?
在系统中,频繁访问的数据往往具有"局部性原理":
最近使用过的数据,未来很可能还会被访问;
很久没访问的数据,可能已经不重要。
为了提高效率,我们希望:
读操作 快;
写入/更新 快;
淘汰旧数据 自动完成。
这就要求:
快速判断数据是否存在;
快速移动"最近访问"的元素;
快速删除"最久未用"的元素。
这类需求,正好可以通过:
哈希表 + 双向链表 来实现。
三、核心数据结构设计
要实现 LRU 缓存,我们需要同时满足:
O(1) 时间内查找;
O(1) 时间内插入与删除。
可以用以下思路:
哈希表(dict)
负责根据 key 快速定位缓存节点;
双向链表
维护数据的访问顺序;
最近使用的节点放在表头;
最久未使用的放在表尾。
当缓存满时,直接删除表尾节点即可,操作效率高。
四、手写实现(核心版)
下面是一份简洁、可直接运行的 LRU 缓存实现。
class Node:
def init(self, key, val):
self.key = key
self.val = val
self.prev = None
self.next = None
class LRUCache:
def init(self, capacity: int):
self.cache = {} # 哈希表
self.capacity = capacity
创建虚拟头尾节点,方便操作
self.head = Node(0, 0)
self.tail = Node(0, 0)
self.head.next = self.tail
self.tail.prev = self.head
移除节点
def _remove(self, node):
prev, nxt = node.prev, node.next
prev.next = nxt
nxt.prev = prev
插入节点到表头(表示最近使用)
def _add(self, node):
node.prev = self.head
node.next = self.head.next
self.head.next.prev = node
self.head.next = node
获取值
def get(self, key: int) -> int:
if key not in self.cache:
return -1
node = self.cache[key]
self._remove(node)
self._add(node)
return node.val
设置值
def put(self, key: int, value: int) -> None:
if key in self.cache:
self._remove(self.cache[key])
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
运行结果:
1
-1
-1
3
4
六、时间与空间复杂度分析
时间复杂度:
所有操作(get、put)都为 O(1),因为查找靠字典,插入/删除靠双向链表。
空间复杂度:
需要额外存储哈希表和双向链表节点,约为 O(n)。
七、Python 内置的 LRU 实现
其实,在实际开发中我们不必手写。
Python 提供了 内置装饰器 functools.lru_cache,可直接实现缓存功能。
示例:
from functools import lru_cache
@lru_cache(maxsize=3)
def add(a, b):
print(f"计算 {a}+{b}")
return a + b
print(add(1, 2)) # 第一次计算
print(add(1, 2)) # 第二次命中缓存,不再计算
输出:
计算 1+2
3
3
⚙️ 参数说明:
maxsize:缓存容量;
lru_cache 会自动根据 LRU 规则清理旧数据。
八、面试高频问法与参考回答
Q1:LRU 缓存为什么用哈希表 + 双向链表?
👉 哈希表保证 O(1) 查找,双向链表保证 O(1) 更新顺序(移动节点)。
Q2:为什么不用单链表?
👉 删除中间节点时需要找到前驱节点,效率低,O(n)。
Q3:如何在 Python 中实现 LRU 缓存?
👉 可手写实现(hash + linked list),或使用 functools.lru_cache 装饰器。
Q4:能否用 OrderedDict 实现?
👉 可以,collections.OrderedDict 支持移动键,非常适合 LRU 缓存:
from collections import OrderedDict
class LRUCache:
def init(self, capacity):
self.cache = OrderedDict()
self.capacity = capacity
def get(self, key):
if key not in self.cache:
return -1
self.cache.move_to_end(key)
return self.cache[key]
def put(self, key, value):
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)
九、总结:回答模板
LRU(最近最少使用)缓存机制的核心思想是:当容量满时,淘汰最久未使用的数据。
在实现上,使用 哈希表 + 双向链表 可以保证 O(1) 的查找、插入与删除效率。
Python 还提供了内置装饰器 functools.lru_cache 或 OrderedDict 可快速实现。
十、结语
LRU 缓存不仅是面试高频题,更是实际开发中常见的性能优化方案。
无论是请求缓存、数据库查询结果缓存,还是函数结果缓存,LRU 都能发挥重要作用。
📌 记忆口诀:
"查找靠哈希,顺序靠链表;淘汰最旧,保留最新。"