LeetCode面试HOT100—— 206. 反转链表

反转链表的头插法解析

反转链表代码超详细解析(头插法思路)

这段代码是反转单链表的 迭代最优解(时间复杂度O(n)、空间复杂度O(1)),核心思路是用「头插法」把原链表的节点逐个"搬到"新链表的头部,最终新链表的头就是反转后的结果。下面从「核心逻辑→逐行拆解→步骤演示→关键细节」帮你彻底吃透。

一、先明确两个核心概念

1. 单链表的结构

每个 ListNode 节点包含两部分:

  • val:节点存储的值;

  • next:指针(引用),指向后一个节点(链表末尾节点的 nextnull)。

原链表示例:1 → 2 → 3 → 4 → nullhead 指向节点1),反转目标:null ← 1 ← 2 ← 3 ← 4(新头指向节点4)。

2. 算法核心思路(头插法)

把原链表的节点 从前往后逐个取出,每次都插入到「新链表的头部」,相当于反向构建新链表:

  • pre 记录「新链表的头节点」(初始为空,因为新链表还没节点);

  • cur 记录「原链表当前要处理的节点」(初始为原链表头 head);

  • nxt 临时保存「原链表的下一个节点」(防止处理当前节点时,原链表"断开");

  • 循环执行:取当前节点→插入新链表头部→移动指针,直到原链表遍历完。

二、代码逐行拆解(带注释)

Java 复制代码

三、分步演示(用示例链表 1→2→3→4→null

我们用表格跟踪每一步 precurnxt 的值和链表结构变化,直观理解反转过程。

初始状态(循环开始前)

指针 指向节点 链表结构(原链表) 新链表结构
pre null 1 → 2 → 3 → 4 → null null
cur 1
nxt 未定义

第一次循环(处理节点1)

  1. ListNode nxt = cur.next;nxt = 2(保存原链表下一个节点);

  2. cur.next = pre; → 节点1的next指向prenull),此时节点1:1→null

  3. pre = cur;pre = 1(新链表头更新为节点1,新链表:1→null);

  4. cur = nxt;cur = 2(原链表当前节点向后移)。

指针 指向节点 原链表剩余部分 新链表结构
pre 1 2 → 3 → 4 → null 1 → null
cur 2
nxt 2

第二次循环(处理节点2)

  1. nxt = cur.next;nxt = 3(保存节点2的下一个节点);

  2. cur.next = pre; → 节点2的next指向pre(1),此时节点2:2→1

  3. pre = cur;pre = 2(新链表头更新为节点2,新链表:2→1→null);

  4. cur = nxt;cur = 3

指针 指向节点 原链表剩余部分 新链表结构
pre 2 3 → 4 → null 2 → 1 → null
cur 3
nxt 3

第三次循环(处理节点3)

  1. nxt = cur.next;nxt = 4

  2. cur.next = pre; → 节点3的next指向2,节点3:3→2

  3. pre = cur;pre = 3(新链表:3→2→1→null);

  4. cur = nxt;cur = 4

指针 指向节点 原链表剩余部分 新链表结构
pre 3 4 → null 3 → 2 → 1 → null
cur 4
nxt 4

第四次循环(处理节点4)

  1. nxt = cur.next;nxt = null(节点4是原链表最后一个节点,下一个为null);

  2. cur.next = pre; → 节点4的next指向3,节点4:4→3

  3. pre = cur;pre = 4(新链表:4→3→2→1→null);

  4. cur = nxt;cur = null(原链表遍历结束,循环终止)。

指针 指向节点 原链表剩余部分 新链表结构
pre 4 null 4 → 3 → 2 → 1 → null
cur null
nxt null

循环结束,返回pre

此时pre指向新链表的头节点4,反转后的链表为 4→3→2→1→null,完全符合预期。

四、关键细节与避坑点

1. 为什么必须用nxt保存cur.next

如果不保存,执行 cur.next = pre 后,cur.next 就指向了新链表(而非原链表的下一个节点),后续无法再找到原链表的剩余节点,导致链表"断开",遍历终止。nxt 相当于"临时备忘录",记录原链表的后续路径。

2. 循环终止条件为什么是cur != null

cur 是原链表的"当前处理节点":

  • cur 不为null时,说明还有节点没处理;

  • curnull时,原链表所有节点都已插入新链表,循环结束。

3. 为什么返回pre而不是cur

循环结束时,cur 已经指向null(原链表遍历完),而 pre 指向的是最后一个处理的节点(原链表的尾节点,新链表的头节点),所以返回pre才是反转后的链表头。

4. 边界情况是否覆盖?

  • 空链表(head = null):cur 初始为null,循环不执行,返回pre = null,正确;

  • 单节点链表(head = 1→null):循环执行1次,pre 最终指向1,返回1,正确。

五、总结

这段代码的核心是「头插法迭代反转」:

  1. 用三个指针(precurnxt)分工明确:pre 管新链表,cur 管原链表当前节点,nxt 管原链表后续节点;

  2. 每一步都围绕"取节点→反转指针→移动指针",逻辑闭环;

  3. 时间复杂度O(n)(遍历一次链表),空间复杂度O(1)(仅用三个指针,不额外占用空间),是最优解法。

记住一句话:"保存下一个,反转当前,移动双指针",就能轻松默写这段代码!如果还是不理解,可以动手画一画每一步的指针变化,可视化后会瞬间清晰。

相关推荐
Qhumaing10 小时前
C++学习:【PTA】数据结构 7-1 实验7-1(最小生成树-Prim算法)
c++·学习·算法
好大哥呀10 小时前
Java Web的学习路径
java·前端·学习
f***147710 小时前
SpringBoot实战:高效实现API限流策略
java·spring boot·后端
on the way 12310 小时前
day06-SpringDI 依赖注入
java·spring
C***115011 小时前
Spring aop 五种通知类型
java·前端·spring
BD_Marathon11 小时前
SpringBoot——多环境开发配置
java·spring boot·后端
代码N年归来仍是新手村成员11 小时前
【Java转Go】即时通信系统代码分析(一)基础Server 构建
java·开发语言·golang
踩坑记录12 小时前
leetcode hot100 3.无重复字符的最长子串 medium 滑动窗口(双指针)
python·leetcode
Z1Jxxx12 小时前
01序列01序列
开发语言·c++·算法