刷 LeetCode Hot100 的时候,看到"23.反转链表"这题,很多人第一反应是:就这?三分钟写完。但我在实际面试中见过太多次,同一个候选人写这三分钟,能写出三种不同版本的 bug。反转链表能排进 Hot100,靠的不是算法难度,而是它用最少的代码行数,考察了你对链表最核心的操作——指针指向的重新编排。这篇从入门到进阶,把迭代、递归、区间反转、K 个一组翻转,以及手撕代码容易踩的坑一次说清。不管你是刚接触链表的初学者,还是准备面试想把手撕代码练稳的老手,都值得认真复盘一遍。
1. 反转链表为什么是Hot100里的"基本功试金石"
1.1 一道看起来简单但暗藏杀机的题目
先看原始题目:给定单链表的头节点 head,反转链表,并返回反转后的链表头节点。
示例:输入 1->2->3->4->5,输出 5->4->3->2->1。
就这么简单的一句话。链表本身的定义也简单:每个节点存一个 val,加一个 next 指针。可就是这道题,让很多候选人在白板前面红耳赤。原因不是不理解"要反转",而是动手写的时候,指针操作的顺序一旦错了,链表就断成一截一截的。
这道题能进 Hot100 的靠前位置,恰恰是因为它把链表最核心的三个能力压缩在一起:找节点、记住后继、改指向。这三个动作的循环复用,就是链表的"乘法口诀"。后面所有链表题,不管包装得多复杂,拆开看都是这三件事。
1.2 链表操作的最小模型
为什么链表操作比数组操作更容易写错?数组是连续内存,arr[i] 和 arr[i+1] 之间的关系是天然存在的,你不用管。链表不一样,每个节点散落在内存各处,节点之间唯一的联系就是 next 指针。你改了一个节点的 next,就等于把这个节点和后继的联系"剪断"了,如果没有提前记住后面的节点,整个链表当场断裂。
反转链表这件事,本质就是:把每个节点的 next 指针从"指向后一个节点"改成"指向前一个节点"。方向全部掉头之后,原来的头变成尾,原来的尾变成头。
把这句话刻在脑子里,后面所有解法都是这句话的工程化实现。理解了这一点,再看迭代法和递归法,就是两种组织"改指针"这个动作的思路而已。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 迭代法:三指针如何一步步"掰弯"链表
2.1 核心思想:让每根指针掉头
迭代法的思路非常朴素:从头开始遍历,每经过一个节点,就把这个节点的 next 改成指向前驱。因为单链表只有 next 指针,没有 prev 指针,所以我们需要自己维护一个 prev 变量来记录当前节点的前驱。
同时,在修改 cur.next 之前,必须先保存 cur 原本的下一个节点。这就是整个算法的核心三步骤:先存后继,再改指向,最后整体前进。
整个过程用一句话概括:把每个节点的"指向后方"掰成"指向前方"。就像一排人排队,每个人都转过身去搭前面一个人的肩膀,队伍整体就反过来了。但每个人转身之前,必须先记住自己身后原来站着谁,不然一转完就找不到下一个了。
2.2 图解三指针的推进过程
以链表 1->2->3->null 为例,文字模拟一遍完整执行过程,你会很清楚指针是怎么移动的。
初始状态:
- prev = null
- cur = 1
- 链表状态:1->2->3->null
第一步:
- next_node = cur.next,也就是 2。
- cur.next = prev,也就是把 1 的 next 置为 null,链表变成 null<-1 2->3->null。
- prev = cur,prev 变成 1。
- cur = next_node,cur 变成 2。
第二步:
- next_node = cur.next,也就是 3。
- cur.next = prev,把 2 的 next 置为 1,链表变成 null<-1<-2 3->null。
- prev = 2。
- cur = 3。
第三步:
- next_node = cur.next,也就是 null。
- cur.next = prev,把 3 的 next 置为 2,链表变成 null<-1<-2<-3。
- prev = 3。
- cur = null。
循环结束,返回 prev,就是新链表的头节点 3。你可以看到,每次循环只做三件事,但链表在一次次循环中逐步"掉头"。
2.3 迭代代码与逐行注释
python复制class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNod
