吸引人的标题手写实现

发布时间:2026/9/23 15:19:18

吸引人的标题手写实现 手写LRU缓存:3道高频面试题,打通底层逻辑 看了一堆教程还是不会写项目?别慌,这不是你的错。很多开发者卡在“懂原理”和“能落地”之间,面试时一提到 高频面试题 里的 LRU 缓存,脑子里全是概念,手却写不出代码。今天不讲虚的,直接拆解 LRU 缓存的核心考点,从算法原理到代码实现,帮你把这块硬骨头啃下来。 考点梳理:LRU 到底考什么? 面试官问 LRU(Least Recently Used,最近最少使用),通常不是只想听你背定义。他们想确认三件事:数据结构选型能力:你知道为什么需要结合哈希表和双向链表? 边界条件处理:容量满时怎么淘汰?键不存在时怎么处理? 性能意识:你能不能说出时间复杂度是 O(1),并解释为什么?很多初学者只记得“链表+哈希表”,但说不清为什么是双向链表而不是单向。这里有个关键细节:单向链表删除节点需要前驱节点,而双向链表可以直接通过节点指针访问前后节点,从而在 O(1) 时间内完成删除。这一点在面试中必须讲清楚,否则会被追问倒。 另外,NPM 官方包 lru-cache 是 JS 生态中实现 LRU 的经典库,其源码逻辑与本文讲解高度一致。研究官方实现,比看十篇博客更有效。你可以去 GitHub 上看 lru-cache 的源码,你会发现它正是用了 Map + 双向链表的变体实现。 标准答法:如何组织语言? 面试时,建议按“总-分-总”结构回答: 第一步:给出结论 “LRU 缓存通常用哈希表 + 双向链表实现,保证 get 和 put 操作都是 O(1) 时间复杂度。” 第二步:解释设计思路哈希表:键为缓存的 key,值为链表中对应节点的指针。用于 O(1) 查找。 双向链表:维护访问顺序。头部是最近使用的,尾部是最久未使用的。 操作逻辑:get(key):如果 key 存在,将对应节点移到头部,返回 value;否则返回 -1。 put(key, value):如果 key 存在,更新 value 并移到头部;如果不存在,新建节点插入头部,若超过容量,删除尾部节点,并同步删除哈希表中的键。第三步:强调优势 “相比数组或普通链表,这种结构避免了 O(n) 的查找或插入开销,特别适合缓存场景。” 注意:不要只说“用哈希表和链表”,必须点明是双向链表,并说明理由。这是区分“背答案”和“真理解”的关键。 代码实现:Python 逐行讲解 下面用 Python 实现一个标准的 LRU 缓存,代码简洁,注释清晰,适合面试手写。 class Node:def __init__(self, key=0, value=0):self.key = keyself.value = valueself.prev = Noneself.next = Noneclass LRUCache:def __init__(self, capacity: int):self.capacity = capacityself.cache = {} # key - Node# 双向链表哨兵节点,避免边界判断self.head = Node()self.tail = Node()self.head.next = self.tailself.tail.prev = self.headdef _remove_node(self, node: Node):从链表中移除节点node.prev.next = node.nextnode.next.prev = node.prevdef _add_to_head(self, node: Node):将节点添加到头部(最近使用)node.next = self.head.nextnode.prev = self.headself.head.next.prev = nodeself.head.next = nodedef get(self, key: int) - int:if key not in self.cache:return -1node = self.cache[key]# 移动到头部,表示最近使用self._remove_node(node)self._add_to_head(node)return node.valuedef put(self, key: int, value: int) - None:if key in self.cache:# 更新值,并移动到头部node = self.cache[key]node.value = valueself._remove_node(node)self._add_to_head(node)else:# 新建节点new_node = Node(key, value)self.cache[key] = new_nodeself._add_to_head(new_node)# 超过容量,淘汰尾部节点if len(self.cache) self.capacity:lru_node = self.tail.prevself._remove_node(lru_node)del self.cache[lru_node.key]逐行解析关键点:哨兵节点(head/tail):避免处理空链表或头尾节点的边界情况,代码更简洁。 _remove_node 和 _add_to_head:封装链表操作,逻辑清晰,便于复用。 put 中的淘汰逻辑:注意先添加新节点,再判断容量,这样保证新节点不会立即被淘汰。 哈希表同步删除:删除尾部节点时,必须同时删除哈希表中对应的键,否则会导致内存泄漏或数据不一致。这段代码在 PyPI 官方包 中虽无直接对应,但逻辑与 functools.lru_cache 装饰器底层实现思路一致。lru_cache 内部也使用了类似的双向链表结构来管理缓存条目。 追问与延伸:面试官还会问什么? 追问1:为什么不用单向链表? 答:单向链表删除节点需要 O(n) 时间找前驱,而双向链表可以 O(1) 删除。在缓存高频读写场景下,性能差异显著。 追问2:如果并发访问,怎么改造? 答:可以加锁,但会降低性能。更优方案是使用线程本地缓存,或采用分段锁。在分布式场景下,可以考虑 Redis 的 LRU 策略,它基于近似算法,适合大规模数据。 追问3:LRU 和 LFU 有什么区别? 答:LRU 淘汰最久未使用的,LFU 淘汰最少使用的。LFU 需要额外记录访问频率,实现更复杂,但适合访问模式不随时间变化的场景。 避坑提醒:手写代码时,不要漏掉哈希表的同步删除,这是最常见的 bug。 测试用例要覆盖:容量为 1、重复 put 相同 key、get 不存在的 key 等边界情况。记忆口诀:快速回忆核心逻辑 为了方便面试前快速回顾,送你一个口诀:哈希查节点,链表管顺序; Get 移头部,Put 先判断; 存在则更新,不存在则新; 超容删尾部,哈希同步删。这四句话涵盖了 LRU 缓存的所有核心操作。面试时,先背口诀,再展开细节,能极大提升表达流畅度。 总结与行动建议 LRU 缓存是 高频面试题 中的经典,但绝非难到无法攻克。关键在于理解“哈希表 + 双向链表”的设计动机,并能手写代码。建议你:亲手敲一遍上面的 Python 代码,不要只看不练。 用测试用例验证,包括边界情况。 对比 NPM/PyPI 官方包的实现,理解工程化细节。这个知识点你面试被问过吗?留言说说,你当时是怎么回答的?有没有被追问到哑口无言?
延伸阅读

更多相关文章

2026/9/23 15:14:15

树莓派CM4底板设计:POE供电与4G模组集成实战

简介:这份资源是树莓派扩展底板 CM-IO-POE-4G-BOX 的完整原理图,面向具备一定电子电路基础的硬件开发者、嵌入式工程师及树莓派爱好者,用于解决底板功能模块识别、电气连接分析与故障定位等问题。压缩包内仅含 1 个 PDF 文件,约 2…

2026/9/23 15:14:15

中国到卢森堡空运哪家好:高货值货物保险与理赔服务对比

高货值货物走中国到卢森堡空运,选服务商时如果只比运价和时效,很可能忽略真正决定损失大小的环节——保险与理赔。卢森堡机场是欧洲主要航空货运枢纽之一,也是多家国际快递与货运航空的重要中转、分拨节点,航线资源相对丰富。但航…

2026/9/23 15:14:15

树莓派CM载板设计:POE供电与4G模组集成原理图实战

简介:这份树莓派底板原理图CM-IO-POE-4G-BOX面向硬件工程师、嵌入式开发者及树莓派进阶玩家,提供CM-IO通用I/O、POE以太网供电与4G无线联网三大功能模块的完整电路设计参考。资源包内含1个PDF文件,约2.6MB,以原理图形式呈现电阻、…

2026/9/23 16:19:28

OpenSpec 规格优先实践:从接口契约到自动化校验的落地指南

1. 从“规格”说起:OpenSpec 到底在解决什么问题第一次听到 OpenSpec 这个名字,很多人会下意识地把它和 OpenAPI、JSON Schema 归到一类,觉得“又是一个写接口文档的规范”。我一开始也是这么想的,直到真正在一个多人协作的中型项…

2026/9/23 16:19:28

AutoJs 4.1.0 Android自动化脚本入门:无障碍服务与控件选择器实战

我第一次听说“clsq客户端”这个名字时,第一反应是某个内部工具,后来被朋友拉到一起折腾才发现,它背后真正有价值的东西其实是基于AutoJs 4.1.0的一套Android自动化脚本方案。AutoJs这个工具在国内Android圈子里名声很大,它是一个…

2026/9/23 16:19:28

AIoT边缘计算网关怎么选?从场景出发,找到最匹配的那一款

选型之前,先别急着看参数很多人选边缘计算网关,第一反应是打开规格书,比CPU核心数、比NPU算力、比接口数量。比着比着就乱了——这个型号算力高但串口少,那个型号串口多但没NPU,还有一个什么都好但价格超预算。正确的顺…

2026/9/23 16:14:27

LM358音频放大电路设计与调试避坑指南

简介:本资源是一份面向电子电路设计初学者与硬件开发者的LM358双运放音频应用实践资料包,聚焦单电源条件下音频信号放大、传感检测与简易报警系统构建等典型场景。内含7款经验证的LM358音频放大电路图(含高灵敏度声音探听器、麦克风前置放大器…

2026/9/23 12:07:00

GAMP 5 基于风险的计算机化系统验证:软件分类与审计追踪实践

简介:《A Risk-Based Approach to Compliant GxP Computerized Systems》即业内熟知的GAMP 5指南,面向制药企业质量与IT合规人员、验证工程师及计算机化系统管理者,用于解决GxP法规环境下系统合规性难以科学落地的问题。文档以风险管理为主线…

2026/9/23 12:06:55

安全托管MSSP实战:从静态防御到人机协同的攻防运营与应急响应

简介:这份PPT围绕互联网业务安全托管服务展开,面向企业安全负责人、IT运维人员及关注MSSP/MSS选型的读者,重点回应传统安全过度依赖人工、碎片化静态防御难以对抗产业化攻击等痛点。资源共1个pptx文件,包体约30.63MB,以…

2026/9/23 0:01:54

3个实战技巧搞定形式英语:从看教程到跑通性能优化

3个实战技巧搞定形式英语:从看教程到跑通性能优化 看了一堆教程还是不会写项目?别慌,这种“眼高手低”的困境在开发者圈子里太常见了。很多人以为卡点在语法,其实真正拦路虎是缺乏将知识点串联成完整链路的能力。今天咱们不聊虚的,直接拿【形式英语】这…

2026/9/22 16:34:32

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/22 20:01:30

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/22 13:25:41

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码