第4章|链表

Unit 02 · LeetCode #206 反转链表

← Unit 01
仅本次页面有效
U02
Reverse Linked ListSave · Reverse · Move · Return

反转链表的难点,不是“让箭头朝左”,
而是改边之前不能丢失未处理区域。

上一单元通过前驱节点绕过目标节点;这一单元继续修改 next,但要逐条反转所有边。每轮都必须严格执行:保存后继 → 反转当前边 → 移动两个指针。

已恢复上次学习位置阶段、动画、代码、迁移脚手架和滚动位置已恢复。
LeetCode #206 题目模型Reverse Linked List · Easy

题目要求

给定单链表的头节点 head,反转链表,并返回反转后的链表头节点。

# ListNode 由 LeetCode 预定义 class Solution: def reverseList( self, head: Optional[ListNode] ) -> Optional[ListNode]: pass

输入、输出与约束

  • 输入:单链表头节点 head;输出:反转后的新头节点。
  • 节点数范围:0–5000;节点值范围:-5000–5000。
  • 目标复杂度:O(n) 时间,迭代版本 O(1) 额外空间。

示例

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

本页主方法

使用 previous / current / next_node。主动画采用显式迭代,因为它把“保存后继”和“修改边”的因果顺序完整暴露出来。

先连接上一题:#203 与 #206 都在改写 next,但修改目标不同

Unit 01:删除节点

current.next = current.next.next
绕过一个节点,前驱通常保持不动。

→
Unit 02:反转边

current.next = previous
改变当前节点的出边方向,必须先保存原后继。

previous / pre

已反转区域的头节点。开始时为空;循环结束时成为整条反转链表的新头。

current / cur

未处理区域的第一个节点,也是本轮即将改变 next 的节点。

next_node

修改 current.next 前保存的原后继。它维持通往剩余链表的唯一入口。

Reachability

任意时刻,全部节点应当能从 previous、current 或临时 next_node 中被找到,不能永久失联。

认知任务 1
从“移动变量”与“修改链表”之间建立边界
沿用 Unit 01 的核心认识:只有改写节点字段,才会真正改变链表结构。
链表与指针动画状态 1 / 1
本步断开 / 覆盖—
本步建立 / 保存—
观察状态
保存后继
反转边
移动 pre
移动 cur
点击下一步,观察指针变量和 next 边分别发生什么变化。

错误 1:先反转,再找后继

current.next 已被覆盖,后续链表入口丢失。

n1 → None  n2 → n3 → n4
但没有变量继续指向 n2

错误 2:移动顺序相反

若先令 current = next_node,再写 previous = current,previous 会跳到未处理节点。

应先:previous = old current
再:current = saved next

错误 3:返回原 head

反转后原 head 已变成尾节点,且其 next 为 None。

返回 head → 只能看到 [1]
返回 previous → [4,3,2,1]
Python 关键表达式补全只补写改变状态的表达式
from typing import Optional class Solution: def reverseList( self, head: Optional[ListNode] ) -> Optional[ListNode]: previous = current = while current: next_node = current.next = previous = current current = return
代码—指针—边逐行联动状态 1 / 1
当前代码行1
previous / currentNone / head
next_node—
已反转链[]
未处理链[1,2,3,4]
进入函数,尚未修改任何边。
完成代码联动后进入 #234 官方原题迁移代码正确或播放完整轨迹后解锁;提示与初始化骨架不会直接解锁。

迁移题:LeetCode #234 回文链表

本次迁移不从完整代码开始,而是先读官方原题,再判断哪些模块来自 Unit 02、哪些是新加入的。核心路线是:快慢指针找中点 → 原地反转后半段 → 同步比较两半。

#234 · Palindrome Linked ListEasy · Linked List · Two Pointers · Stack · Recursion打开 LeetCode 官方原题 ↗

阶段 1:先确认官方题目到底要求什么

官方题意

给定单链表头节点 head,判断该链表的节点值序列是否构成回文。回文意味着从前向后与从后向前读取完全相同。

from typing import Optional class Solution: def isPalindrome( self, head: Optional[ListNode] ) -> bool: pass
进阶目标

使用 O(n) 时间与 O(1) 额外空间完成判断。

官方示例
输入:head = [1,2,2,1]
输出:true
输入:head = [1,2]
输出:false
官方约束
  • 节点数在 [1, 10⁵];
  • 0 ≤ Node.val ≤ 9;
  • 返回布尔值,不返回反转后的链表。
输出契约

答案是 True / False,不是链表或中点。

顺序限制

单链表不能从尾部向前读取,需要制造可比较方向。

空间进阶

不能把所有值复制到数组后再双指针比较。

复用入口

反转后半段后,两半都能沿 next 向前读取。

读题检查 1:输入 [1,2,2,1] 应返回什么?
先看函数返回类型。
读题检查 2:要达到 O(1) 额外空间,最适合复用什么?
关注哪些方法会保存 O(n) 个值或调用帧。
两道读题检查均正确后进入结构对照

阶段 2:#206 是反转模块,#234 是三个模块的组合

维度#206 反转链表#234 回文链表
最终目标返回整条链表的新头返回节点值是否前后对称
已知模块reverse(head)把 reverse 用在后半段
新增状态previous/current/next_nodeslow/fast 找中点,first/second 比较
结构修改反转全部 next 边只反转后半段,比较时只读 val
复杂度O(n) / O(1)O(n) / O(1)

请按真实执行顺序点击三个模块。顺序错误时可以重置后重试。

当前顺序:尚未选择
调用 reverse(slow) 后,slow 在反转函数内部扮演什么角色?
把 slow 看成传入辅助函数的参数 start。
正确排序并完成变量映射后继续

阶段 3:只填写四个新增语义,不重新发明 reverse

反转辅助函数沿用 Unit 02。这里重点补充 #234 新增的“中点、调用位置、比较与返回”语义。

槽位 1 · 找中点快慢指针循环
槽位 2 · 复用反转反转后半段入口
槽位 3 · 同步比较比较两半并移动指针
槽位 4 · 成功出口全部匹配后的返回值
建议先写语义,再考虑缩进和完整函数。
from typing import Optional

class Solution:
    def isPalindrome(self, head: Optional[ListNode]) -> bool:
        def reverse(start):
            previous = None
            current = start
            while current:
                next_node = current.next
                current.next = previous
                previous = current
                current = next_node
            return previous

        slow = fast = head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next

        second = reverse(slow)
        first = head
        while second:
            if first.val != second.val:
                return False
            first = first.next
            second = second.next
        return True
四个槽位正确后展示组装代码

阶段 4:运行官方样例 [1,2,2,1],再独立重写

官方样例轨迹
slow / fasthead / head
first / second— / —
当前结论处理中
从原链表开始,先用快慢指针定位后半段入口。
独立重写:O(n) 时间、O(1) 额外空间

不要复制组装代码。独立写出找中点、反转后半段和比较两半。

轨迹走完后,再独立完成代码。

#206 → #234 迁移结论

迁移并不是把整题重新背一遍,而是把稳定的 reverse Skill 嵌入新流程。新增知识只有两块:快慢指针确定后半段入口,以及两条正向链表的同步比较。

Unit 02 Skill:在保持剩余区域可达的前提下逐条反转边

每轮先保存 next_node = current.next,再令 current.next = previous,随后依次移动 previous 和 current。循环结束时 current 为 None,previous 指向新头节点。

与上一题联系真正改变结构的是 node.next
核心表示previous / current / next_node
循环不变量已反转区 + 未反转区覆盖全部节点
关键顺序Save → Reverse → Move → Move
常见错误改边前未保存原后继
复杂度O(n) 时间,O(1) 额外空间
本单元完成后会停留在这里

不会自动进入 Unit 03。可以继续调整答案框、回看任何阶段,刷新后仍恢复当前位置。

手动进入 Unit 03 →
阶段 1 / 6 · 仅本次页面有效,可随时返回