如果你正在准备 Python 开发面试,链表是必须掌握的一类题。
它看起来不像数组那样直观,但只要掌握几个固定套路,拿分会非常稳定。
这一篇延续前两篇的突击风格:
少而精,只练最经典、最常考、最容易被追问的两题。
为什么 Linked List 高频出现?
面试官喜欢链表题,不是因为它"难",而是因为它能快速区分候选人的基本功:
- 是否真正理解指针/引用关系;
- 能否在不借助额外数组的情况下原地操作;
- 能否稳定处理
None、空链表、单节点等边界; - 写代码时是否清楚每一步指针移动。
如果你只有 30 分钟,建议优先吃透这两题:
Reverse Linked ListMerge Two Sorted Lists
它们分别覆盖两种核心技巧:
- 指针反转(迭代 + 递归)
- dummy 虚拟头节点 + 双指针拼接
今天的突击目标
只要做到下面四点,这一篇就算学到位了:
- 能手写并讲清楚
Reverse Linked List的迭代与递归; - 能说清
Merge Two Sorted Lists里dummy的价值; - 能在讲解时明确时间/空间复杂度;
- 能主动指出易错点,尤其是指针断链问题。
题目一:Reverse Linked List(反转链表)
题目描述
给定单链表头节点 head,将链表反转并返回反转后的头节点。
示例:
- 输入:
[1,2,3,4,5],输出:[5,4,3,2,1] - 输入:
[1,2],输出:[2,1] - 输入:
[],输出:[]
面试中的思考路径
这题的 follow-up 常问:
"你能分别用迭代和递归实现吗?"
解法一:迭代(首选,最稳)
迭代核心是三个指针变量:
prev:当前节点反转后要指向的前驱;curr:当前处理节点;nxt:提前保存curr.next,防止断链后找不到后续节点。
Python 参考实现(迭代)
python
from typing import Optional
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
prev = None
curr = head
while curr:
nxt = curr.next
curr.next = prev
prev = curr
curr = nxt
return prev
解法二:递归(面试加分)
递归写法的关键点是:
- 递归到底(到尾节点)后,尾节点会成为新头;
- 回溯时把后一个节点的
next指回当前节点。
Python 参考实现(递归)
python
from typing import Optional
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
if head is None or head.next is None:
return head
new_head = self.reverseList(head.next)
head.next.next = head
head.next = None
return new_head
递归易错点
很多人会写成 new_head.next = head,这种写法通常是不对的,因为 new_head 指向的是"新头",并不总是"当前节点的下一个节点"。
正确做法必须基于当前层关系写成:
head.next.next = headhead.next = None
这样才能保证每一层回溯都正确翻转,并且不会形成环。
和斐波那契递归有什么区别?
这点面试里讲出来很加分:
- 斐波那契常见写法是分叉递归(一个函数拆成两个子问题),有大量重复计算;
- 反转链表是单链递归(每层只递归一次),没有分叉爆炸;
- 因此链表递归更接近"沿链到底,再回溯改指针"的过程,逻辑更线性。
复杂度分析
- 迭代:时间
O(n),空间O(1); - 递归:时间
O(n),空间O(n)(递归调用栈)。
题目二:Merge Two Sorted Lists(合并两个有序链表)
题目描述
给定两个升序链表 list1 和 list2,将它们合并为一个新的升序链表并返回头节点。
要求通过"拼接原节点"的方式完成,不是新建值数组再重建链表。
示例:
- 输入:
list1=[1,2,4], list2=[1,3,4],输出:[1,1,2,3,4,4] - 输入:
list1=[], list2=[],输出:[] - 输入:
list1=[], list2=[0],输出:[0]
面试中的思考路径
这题最稳定写法是 dummy + cur:
dummy是虚拟头节点,避免处理"第一个节点到底挂谁"的分支复杂度;cur始终指向合并链表的尾部;- 每次比较
list1.val与list2.val,把较小节点接到cur.next; - 某一条链表走完后,直接把另一条剩余部分接上。
Python 参考实现
python
from typing import Optional
class Solution:
def mergeTwoLists(
self, list1: Optional[ListNode], list2: Optional[ListNode]
) -> Optional[ListNode]:
dummy = ListNode()
cur = dummy
while list1 and list2:
if list1.val < list2.val:
cur.next = list1
list1 = list1.next
else:
cur.next = list2
list2 = list2.next
cur = cur.next
cur.next = list1 if list1 else list2
return dummy.next
复杂度分析
- 时间复杂度:
O(m+n),两个链表各走一遍; - 空间复杂度:
O(1),只使用常数额外指针。
面试易错点
- 不用
dummy,导致头节点处理写出很多 if-else; - 比较后忘记移动
cur; - 循环结束后忘记拼接剩余链表;
- 误新建大量节点,偏离"拼接原链表节点"的意图。
30 分钟高效练习法(Linked List 版)
按这个节奏走,效率很高:
- 5 分钟:先口述两题指针变化过程;
- 10 分钟 :手写
reverseList(迭代 + 递归); - 10 分钟 :手写
mergeTwoLists,重点练dummy; - 5 分钟:不看代码复述复杂度和易错点。
链表题最怕"看懂了但写不对",所以一定要手写。
面试表达模板(可直接套用)
你可以这样说:
这题我会优先考虑指针原地操作,避免不必要的额外空间。
如果涉及链表拼接,我会引入 dummy 节点统一处理头节点逻辑,减少分支。
在实现上我会先保证不断链,再改指向,最后移动指针。
复杂度方面,目标通常是线性时间,额外空间尽量保持常数级。
这段话很适合链表题开场,能快速体现你的工程化思维。
小结
Linked List 的关键不是"记住答案",而是形成稳定的指针操作习惯。
把 Reverse Linked List 和 Merge Two Sorted Lists 吃透后,你会对链表题建立很强的控制感。
面试前请至少达到这个标准:
- 迭代反转和递归反转都能默写;
- 能解释为什么
head.next.next = head是关键; dummy + cur写法一遍过;- 复杂度和边界条件能清楚表达。
下一篇可以继续进入 Trees,把链表和树这两类"指针结构"连起来,面试中的数据结构题会更稳。
支持一下
如果这篇对你有帮助,欢迎点赞、收藏、关注。 有余力的话欢迎打赏支持,我会更新得更快。