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

📌 记忆口诀:

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

相关推荐
如何原谅奋力过但无声1 分钟前
TensorFlow 1.x常用函数总结(持续更新)
人工智能·python·tensorflow
翔云 OCR API4 分钟前
人脸识别API开发者对接代码示例
开发语言·人工智能·python·计算机视觉·ocr
AndrewHZ1 小时前
【图像处理基石】如何在图像中提取出基本形状,比如圆形,椭圆,方形等等?
图像处理·python·算法·计算机视觉·cv·形状提取
温轻舟2 小时前
Python自动办公工具05-Word表中相同内容的单元格自动合并
开发语言·python·word·自动化办公·温轻舟
习习.y3 小时前
python笔记梳理以及一些题目整理
开发语言·笔记·python
撸码猿3 小时前
《Python AI入门》第10章 拥抱AIGC——OpenAI API调用与Prompt工程实战
人工智能·python·aigc
程序员小寒3 小时前
前端高频面试题之CSS篇(一)
前端·css·面试·css3
qq_386218993 小时前
Gemini生成的自动搜索和下载论文的python脚本
开发语言·python
vx_vxbs663 小时前
【SSM电影网站】(免费领源码+演示录像)|可做计算机毕设Java、Python、PHP、小程序APP、C#、爬虫大数据、单片机、文案
java·spring boot·python·mysql·小程序·php·idea
进击的野人5 小时前
深入理解 JavaScript Promise:原理、用法与实践
javascript·面试·ecmascript 6