题目描述

✅ 剑指 Offer 24. 反转链表

image-20261001230752547

image-20260928181518689

image-20260928181518690

题意分析

给定单链表的头节点,将所有节点的后继方向反转,并返回新的头节点。原来的尾节点会成为新头,原来的头节点会成为新尾,节点本身及其值保持不变。

这是对链表连接的修改,不是只把节点值倒序输出。空链表仍返回空,单节点链表的结果仍是它自己;对于普通链表,需要在改变连接时保住尚未处理的剩余部分。

解法:迭代三指针反转

核心思路

[!blue]

把处理过程分为两段:pre 指向已经反转好的前缀,cur 指向尚未处理后缀的第一个节点。开始时已反转部分为空,所以 pre = null,未处理部分就是整条原链,所以 cur = head。

每轮从未处理部分取出当前节点,接到已反转部分的头部。首先用 next 保存原来的 cur.next,它是剩余后缀的入口;然后执行 cur.next = pre,把当前节点的方向改为指向前面已经反转的链。

接着令 pre = cur,让已反转部分包含刚处理的节点,再令 cur = next,继续处理原来的后继。每轮都恰好把一个节点从未处理部分转移到已反转部分,两段仍包含原链的全部节点,不会丢失或重复处理。

第一次反转时,原头的后继会被设为空,因此它最终成为正确的尾节点,不会残留向后的旧连接。之后每个当前节点都只指向已经处理的节点,也不会形成环。

当 cur 为空时,未处理部分已经耗尽,pre 就是整条反转链的头。原 head 此时指向尾节点,不能作为返回结果;空链表和单节点也会按同一流程得到正确返回值。

解题步骤

  1. 初始化 pre = null、cur = head。
  2. 当 cur 非空时,先将原后继保存在 next 中。
  3. 执行 cur.next = pre,反转当前节点的后继方向。
  4. 依次更新 pre = cur、cur = next,扩大已反转部分并继续后缀。
  5. 循环结束后返回 pre。

代码实现

class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode pre = null;
        ListNode cur = head;

        while (cur != null) {
            ListNode next = cur.next;

            // 当前节点指回已经反转好的前半部分。
            cur.next = pre;
            pre = cur;
            cur = next;
        }

        return pre;
    }
}
func reverseList(head *ListNode) *ListNode {
    var pre *ListNode
    cur := head
    for cur != nil {
        next := cur.Next
        // 保存 next 后再改变指针方向。
        cur.Next = pre
        pre = cur
        cur = next
    }
    return pre
}

复杂度分析

  • 时间复杂度:$O(n)$,每个节点恰好处理一次,只进行常量次指针读取和修改。
  • 空间复杂度:$O(1)$,只使用 pre、cur、next 三个工作指针,不创建新节点或递归栈。

关键点总结

[!green]

  • pre 与 cur 分别代表已反转前缀和未处理后缀,每轮只转移一个节点。
  • 保存原后继必须先于修改当前后继,才能继续访问未处理部分。
  • pre 初始为空负责断开原头的旧后继,结束时 pre 又负责提供新头。

易错点总结

[!yellow]

  • 先改写 cur.next 再保存它,得到的已经是反向连接,会丢掉原来的后缀入口。
  • 改写连接后直接令 cur = cur.next,会沿反转后的边走回前缀,应使用之前保存的 next。
  • 循环判断 cur.next != null,既会漏掉最后一个节点,也无法安全处理空链表,应判断 cur 本身。
  • 将 pre 初始化为 head,第一次操作会让原头指向自身形成环。
  • 返回原 head 或退出时的 cur 都不对,前者是新尾,后者为空,正确返回值是 pre。

相似题目

题目 难度 关联与区别
92. 反转链表 II 中等 把整条链表反转限制到给定区间,额外保存区间前驱并接回两端。
25. K 个一组翻转链表 困难 把反转作为子过程,每k个节点处理一次,并保留不足k的尾段。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2025/27429259
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!