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)(仅用三个指针,不额外占用空间),是最优解法。

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

相关推荐
大布布将军3 分钟前
⚡️ 深入数据之海:SQL 基础与 ORM 的应用
前端·数据库·经验分享·sql·程序人生·面试·改行学it
醒过来摸鱼5 分钟前
Java classloader
java·开发语言·python
专注于大数据技术栈7 分钟前
java学习--StringBuilder
java·学习
loosenivy11 分钟前
企业银行账户归属地查询接口如何用Java调用
java·企业银行账户归属地·企业账户查询接口·企业银行账户查询
IT 行者33 分钟前
Spring Security 6.x 迁移到 7.0 的完整步骤
java·spring·oauth2
JIngJaneIL36 分钟前
基于java+ vue农产投入线上管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot
东东的脑洞39 分钟前
【面试突击二】JAVA基础知识-volatile、synchronized与ReentrantLock深度对比
java·面试
LYFlied43 分钟前
【每日算法】LeetCode 153. 寻找旋转排序数组中的最小值
数据结构·算法·leetcode·面试·职场和发展
唐装鼠1 小时前
rust自动调用Deref(deepseek)
开发语言·算法·rust
川贝枇杷膏cbppg1 小时前
Redis 的 AOF
java·数据库·redis