迭代双指针更常用,因其空间复杂度稳定为O(1)、不易爆栈、逻辑清晰且调试方便;递归虽简洁但栈空间O(n),仅适用于极短链表或教学场景。

为什么迭代双指针比递归更常用
因为递归翻转在链表很长时容易爆栈,而迭代写法空间复杂度稳定为 O(1),且逻辑清晰、调试方便。实际工程里几乎都用迭代——除非你明确知道链表长度不会超过几百。
常见错误现象:AttributeError: 'NoneType' object has no attribute 'next',多半是没处理好 prev 和 curr 的初始赋值顺序,或者循环条件写成 curr.next is not None 导致漏掉最后一个节点。
- 必须从
prev = None、curr = head开始,不能反过来 - 循环条件用
while curr is not None,不是curr.next - 核心三步要严格按顺序:暂存
next_node = curr.next→ 反向连接curr.next = prev→ 移动指针prev, curr = curr, next_node
def reverse_list(head):
prev, curr = None, head
while curr is not None:
next_node = curr.next
curr.next = prev
prev, curr = curr, next_node
return prev
递归翻转的边界和调用时机
递归本质是靠系统栈“记住”尚未处理的节点,等递归到底部(head.next is None)再一层层回退时改指针。它看起来简洁,但容易忽略两个关键点:一是返回值必须是新头节点(即原尾节点),二是改指针的动作必须发生在递归调用之后。
使用场景:仅限教学演示、链表极短(如 LeetCode 示例)、或你在写函数式风格代码且确定栈深可控。
立即学习“Python免费学习笔记(深入)”;
- 递归终止条件必须是
if not head or not head.next,缺一不可,否则空链表会报错 -
new_head = reverse_list(head.next)必须先执行,拿到最终头节点 - 改指针动作
head.next.next = head和head.next = None必须放在递归调用之后 - 性能影响:时间
O(n),空间O(n)(栈深度),Python 默认递归限制约 1000 层
def reverse_list(head):
if not head or not head.next:
return head
new_head = reverse_list(head.next)
head.next.next = head
head.next = None
return new_head
反转后怎么验证结果是否正确
别只看返回值不为空就以为成功了。真实项目中常因指针没断干净导致环形链表,后续遍历直接死循环。
最容易被忽略的是:反转后原 head 节点的 next 字段仍指向旧节点,必须显式置为 None(迭代中由 curr.next = prev 自然完成;递归中需手动加 head.next = None)。
- 用快慢指针跑一遍检测是否有环:
has_cycle(new_head) - 打印前 5 个节点值,确认顺序倒过来了
- 检查原
head.next是否为None(尤其递归实现里常漏这步) - 如果链表带哨兵节点或有额外字段(如
size),记得同步更新
LeetCode 提交时要注意的 Python 细节
题目给的 ListNode 类型没有内置 __repr__,本地调试时看不到结构,容易误判结果。另外,有些题测试用例包含空链表或单节点,边界处理不到位就会挂。
参数差异:函数签名固定为 def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:,注意类型提示里的 Optional 意味着 head 可能是 None。
- 空链表输入必须返回
None,不能返回ListNode(None) - 不要在代码里 print 或引入额外模块(如
sys),OJ 会报错 - 递归解法在 LeetCode 上可能触发“最大递归深度超限”,尤其 Python3 默认限制低
- 迭代解法中避免用
is比较None,统一用is None或is not None
prev 和 curr 的初始状态,以及 head.next = None 这种看似多余却保命的操作。










