LeetCode 第 206 题「反转链表」
字数 1861 2025-10-25 17:32:51

好的,我们这次来详细讲解 LeetCode 第 206 题「反转链表」


1. 题目描述

题目:给你单链表的头节点 head,请你反转链表,并返回反转后的链表。

示例 1
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

示例 2
输入:head = [1,2]
输出:[2,1]

示例 3
输入:head = []
输出:[]

链表定义(题目已给出):

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

2. 题意理解

链表是由节点组成的,每个节点保存一个值和一个指向下一个节点的指针。
初始状态:

1 → 2 → 3 → 4 → 5 → None

反转后要变成:

5 → 4 → 3 → 2 → 1 → None

要求

  • 只使用常数额外空间(即 O(1) 额外空间,递归栈空间不算常数的特殊情况这里先不讨论递归版本的空间问题)。
  • 不得改变节点内部的值,只能改变节点的 next 指针。

3. 思路演进

3.1 直观想法(但不可行)

如果允许修改节点的值,我们可以先遍历链表把值存到数组,再反向填入链表。但题目一般期望直接通过指针操作完成。

3.2 关键观察

反转链表的过程,可以想象成一边遍历原链表,一边把每个节点指向前一个节点。

我们需要:

  1. 记录当前节点 cur 和前一个节点 prev
  2. 在改变 cur.next 之前,先保存 cur 原本的下一个节点 nxt,否则会丢失后续链表。
  3. cur.next 指向 prev
  4. prevcur 都向前移动一步。

4. 逐步推导

我们以 head = [1,2,3,4,5] 为例。

初始状态

prev = None
cur = 1 → 2 → 3 → 4 → 5 → None

第 1 步

  • nxt = cur.nextnxt = 2
  • cur.next = prev1 → None
  • prev = curprev = 1
  • cur = nxtcur = 2

此时链表被拆成两部分:
反转部分:1 → None(prev 指向 1)
未反转部分:2 → 3 → 4 → 5 → None(cur 指向 2)

第 2 步

  • nxt = cur.nextnxt = 3
  • cur.next = prev2 → 1
  • prev = curprev = 2
  • cur = nxtcur = 3

此时:2 → 1 → None(prev 指向 2),未反转部分 3 → 4 → 5 → None

第 3 步

  • nxt = 4
  • cur.next = prev3 → 2
  • prev = 3
  • cur = 4

此时:3 → 2 → 1 → None

第 4 步

  • nxt = 5
  • cur.next = prev4 → 3
  • prev = 4
  • cur = 5

此时:4 → 3 → 2 → 1 → None

第 5 步

  • nxt = None
  • cur.next = prev5 → 4
  • prev = 5
  • cur = None

此时:5 → 4 → 3 → 2 → 1 → NonecurNone,循环结束。

最终返回 prev,即新链表的头节点 5


5. 代码实现(迭代法)

def reverseList(head: ListNode) -> ListNode:
    prev = None
    cur = head
    while cur:
        # 保存下一个节点
        nxt = cur.next
        # 反转指针
        cur.next = prev
        # 移动 prev 和 cur
        prev = cur
        cur = nxt
    return prev

复杂度分析

  • 时间复杂度:O(n),遍历一次链表。
  • 空间复杂度:O(1),只用了几个指针变量。

6. 递归解法(辅助理解)

递归思路:
假设头节点是 head,剩余部分 head.next 已经通过递归被正确反转了,那么我们只需要把 head 放到已反转部分的末尾。

具体:

  1. 递归基:如果 headNonehead.nextNone,直接返回 head
  2. 递归反转 head.next 开始的链表,得到新头节点 new_head,此时 head.next 是新链表的尾节点。
  3. head 接在 head.next 的后面:head.next.next = head
  4. head.next 设为 None,避免成环。
  5. 返回 new_head

代码:

def reverseListRecursive(head: ListNode) -> ListNode:
    if not head or not head.next:
        return head
    new_head = reverseListRecursive(head.next)
    head.next.next = head
    head.next = None
    return new_head

递归过程需要画图理解指针变化,但迭代法更直观且空间更优。


7. 总结

  • 核心:用两个指针 prevcur 一边遍历一边反转。
  • 关键:在修改 cur.next 前先保存 cur.next
  • 结果:最后 prev 就是新链表的头节点。

这样讲解是否清晰?如果需要我画出每一步的链表指针变化图,我也可以为你绘制。

好的,我们这次来详细讲解 LeetCode 第 206 题「反转链表」 。 1. 题目描述 题目 :给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。 示例 1 : 输入: head = [1,2,3,4,5] 输出: [5,4,3,2,1] 示例 2 : 输入: head = [1,2] 输出: [2,1] 示例 3 : 输入: head = [] 输出: [] 链表定义 (题目已给出): 2. 题意理解 链表是由节点组成的,每个节点保存一个值和一个指向下一个节点的指针。 初始状态: 反转后要变成: 要求 : 只使用常数额外空间(即 O(1) 额外空间,递归栈空间不算常数的特殊情况这里先不讨论递归版本的空间问题)。 不得改变节点内部的值,只能改变节点的 next 指针。 3. 思路演进 3.1 直观想法(但不可行) 如果允许修改节点的值,我们可以先遍历链表把值存到数组,再反向填入链表。但题目一般期望直接通过指针操作完成。 3.2 关键观察 反转链表的过程,可以想象成一边遍历原链表,一边把每个节点指向前一个节点。 我们需要: 记录当前节点 cur 和前一个节点 prev 。 在改变 cur.next 之前,先保存 cur 原本的下一个节点 nxt ,否则会丢失后续链表。 将 cur.next 指向 prev 。 prev 和 cur 都向前移动一步。 4. 逐步推导 我们以 head = [1,2,3,4,5] 为例。 初始状态 : 第 1 步 : nxt = cur.next 即 nxt = 2 cur.next = prev 即 1 → None prev = cur 即 prev = 1 cur = nxt 即 cur = 2 此时链表被拆成两部分: 反转部分: 1 → None (prev 指向 1) 未反转部分: 2 → 3 → 4 → 5 → None (cur 指向 2) 第 2 步 : nxt = cur.next 即 nxt = 3 cur.next = prev 即 2 → 1 prev = cur 即 prev = 2 cur = nxt 即 cur = 3 此时: 2 → 1 → None (prev 指向 2),未反转部分 3 → 4 → 5 → None 第 3 步 : nxt = 4 cur.next = prev 即 3 → 2 prev = 3 cur = 4 此时: 3 → 2 → 1 → None 第 4 步 : nxt = 5 cur.next = prev 即 4 → 3 prev = 4 cur = 5 此时: 4 → 3 → 2 → 1 → None 第 5 步 : nxt = None cur.next = prev 即 5 → 4 prev = 5 cur = None 此时: 5 → 4 → 3 → 2 → 1 → None , cur 为 None ,循环结束。 最终返回 prev ,即新链表的头节点 5 。 5. 代码实现(迭代法) 复杂度分析 : 时间复杂度:O(n),遍历一次链表。 空间复杂度:O(1),只用了几个指针变量。 6. 递归解法(辅助理解) 递归思路: 假设头节点是 head ,剩余部分 head.next 已经通过递归被正确反转了,那么我们只需要把 head 放到已反转部分的末尾。 具体: 递归基:如果 head 是 None 或 head.next 是 None ,直接返回 head 。 递归反转 head.next 开始的链表,得到新头节点 new_head ,此时 head.next 是新链表的尾节点。 把 head 接在 head.next 的后面: head.next.next = head 将 head.next 设为 None ,避免成环。 返回 new_head 。 代码: 递归过程需要画图理解指针变化,但迭代法更直观且空间更优。 7. 总结 核心:用两个指针 prev 和 cur 一边遍历一边反转。 关键:在修改 cur.next 前先保存 cur.next 。 结果:最后 prev 就是新链表的头节点。 这样讲解是否清晰?如果需要我画出每一步的链表指针变化图,我也可以为你绘制。