Python 数据结构面试真题:如何实现 LRU 缓存机制

在 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 都能发挥重要作用。

📌 记忆口诀:

"查找靠哈希,顺序靠链表;淘汰最旧,保留最新。"

相关推荐
召摇4 小时前
在浏览器中无缝运行Go工具:WebAssembly实战指南
后端·面试·go
召摇4 小时前
Spring Security入门指南
后端·spring·面试
星期天要睡觉4 小时前
深度学习——基于 ResNet18 的图像分类训练
pytorch·python·机器学习
林炳然4 小时前
Python-Basic Day-1 基本元素(数字、字符串)
python
weixin_307779134 小时前
在Linux服务器上使用Jenkins和Poetry实现Python项目自动化
linux·开发语言·python·自动化·jenkins
今天没有盐4 小时前
内置基础类型之布尔值类型(bool)与时间与日期类型
python·编程语言
Empty_7774 小时前
Python编程之常用模块
开发语言·网络·python
Q_Q5110082854 小时前
python+uniapp基于微信小程序的学院设备报修系统
spring boot·python·微信小程序·django·flask·uni-app
蓝色空白的博客5 小时前
自动化测试脚本-->集成测试部署思路整理(1)
python·集成测试