206. 反转链表 
实现思路
两个两个依次交换,交换的时候需要有一个临时变量记录剩下的链表,交换完成后将 pre 和 cur 向后移动一位,继续交换,直到 cur 为空。
- 定义两个指针 pre 和 cur,pre 初始化为 null,cur 初始化为 head。
- 遍历链表,将 cur 的 next 指向 pre,然后将 pre 和 cur 向后移动一位。
- 遍历结束后,pre 指向新的链表头节点。
代码实现
易错点:反转链表不需要虚拟头节点
普通链表反转不需要虚拟头节点。反转过程中,pre 本身就在维护“已经反转部分”的头节点;循环结束后,pre 也正好是整个链表的新头节点。
初始状态下:
每轮循环把 cur 指向的节点移动到已反转部分的头部:
原链表头节点 1 反转后会成为尾节点,所以第一次执行 cur.next = pre 时,应该让 1.next 指向 null。这正是将 pre 初始化为 null 的原因。
错误写法一:在循环内部初始化 pre
这段代码每轮只处理一个节点,并且每次都会将 pre 重新设置为 null。以 1 -> 2 -> 3 为例,第一轮执行 cur.next = pre 后,链表会被拆成:
随后 p 移动到节点 1,由于 p.next 已经是 null,循环直接结束。后面的 2 -> 3 不仅没有被反转,还与返回结果断开了。
此外,cur = temp 也没有发挥作用,因为本轮循环马上结束;即使存在下一轮,cur 也会在循环内部被重新声明。
错误写法二:让 pre 指向虚拟头节点
假设原链表为 1 -> 2 -> 3,初始时:
第一次执行:
就会同时出现:
因此 dummy 和节点 1 形成了环:
而且 dummy.next 始终指向原头节点 1。循环结束后,真正的新头节点是 pre 指向的节点 3,返回 dummy.next 不但返回了错误节点,还会得到一个带环的链表。
正确写法的三个关键点
pre必须定义在循环外,保存整个反转过程中已经处理好的链表。pre必须初始化为null,确保原头节点最终成为正确的尾节点。- 循环结束后必须返回
pre,因为它指向反转后的新头节点。
虚拟头节点通常适合删除节点、在头部插入节点等需要统一处理头节点的场景。反转链表已经可以用 pre 接住不断变化的新头节点,引入虚拟头节点不会简化逻辑,使用不当反而容易产生环或返回错误节点。


