题目描述
把单链表整个反转:每个节点的指针都指向它原来的前一个。
思路解析
可以遍历把值压进数组/栈,再倒着 new 一条新链表——但要 O(n) 额外空间,还白白新建了节点。更优雅的是原地:只改指针指向,O(1) 空间。
prev 跟在后面(初始 null)、cur 是当前节点。每一轮都做四个动作:①用 next 记住下一个(防断链);② 把 cur.next 掉头指向 prev(原来的箭头先断开、再接到 prev);③ prev 前进一步;④ cur 前进一步。cur 走到 null 时,prev 就是新头。下面一帧一个动作慢放。
起点 · prev=null, cur=头:cur 指向头节点 1,prev 此刻是 null(空)。准备把箭头一根根掉头。
① 记住 next = 2:动手前先用 next 抓住 2。一旦把 1 的箭头改了,就再也找不到后面——「先记 next」是不断链的命根子。
② cur.next 掉头 → prev(null):执行 cur.next = prev:1 原来指向 2 的箭头先断开(图上变成 ·),改指向 prev=null。还好 next 仍抓着 2,没丢。
③④ prev、cur 各前进一步:prev 来到刚处理完的 1、cur 来到 2。第一轮四步走完,1 已经「翻好」(它的箭头朝向 null)。
① 记住 next = 3:第二轮,同样先用 next 记住 3。
② cur.next 掉头 → prev(1):cur.next = prev:2 原来指向 3 的箭头断开(右边变 ·),改指向 prev=1——于是 2 → 1(左边箭头变 ←)。next 仍抓着 3。
③④ prev、cur 各前进:prev 前进到 2、cur 前进到 3。第二根也翻好了。
① 记住 next = null:第三轮,记 next = 3 的下一个,发现是 null——说明 3 是原来的最后一个节点,这会是最后一轮。
② cur.next 掉头 → prev(2):cur.next = prev:3 改指向 prev=2,3 → 2(右边箭头变 ←)。三根箭头现在全部掉头完毕。
③④ 完成 · cur=null, prev=新头:prev 前进到 3、cur 前进到 null,循环结束。此刻 prev 停在 3——它就是反转后的新头:3 → 2 → 1 → null。
链表操作的通用心法:改指针前,先用临时变量抓住会丢失的那一端。反转、删除、合并都靠它,绝不能让链断在手里。
参考代码(Python)
1def reverseList(self, head):
2 prev, cur = None, head
3 while cur:
4 nxt = cur.next # ① 记住下一个
5 cur.next = prev # ② 箭头掉头
6 prev = cur # ③ prev 前进
7 cur = nxt # ④ cur 前进
8 return prev # prev 是新头复杂度分析
- 时间复杂度:O(n) —— 每个节点走一次
- 空间复杂度:O(1) —— 只用三个指针
套路模板
记住骨架:prev 跟随、先存 nxt、再改 cur.next、最后双双前进。把「改 cur.next」换成不同操作,就能做删除、去重、两两交换。
1# 凡是「一边遍历一边改指针」的链表题都套
2prev, cur = None, head
3while cur:
4 nxt = cur.next # 先抓住后面,防断链
5 ... # 改 cur.next(掉头/跳过/接走)
6 prev, cur = cur, nxt # 一起前进一步易错点
- ❌ 先 cur.next=prev 再记 next → ✅ 必须先 nxt=cur.next(顺序反了,改完就找不到后面,链断了)
- ❌ 返回 cur → ✅ 返回 prev(循环结束时 cur 已是 null,prev 才是新头)
- ❌ prev 初始化成 head → ✅ prev 初始为 None(原来的头反转后要指向 null)
以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握。