K 个一组翻转链表
Reverse Nodes in k-Group
每 k 个节点一组翻转链表;最后不足 k 个的节点保持原来的顺序。
在力扣中练习题目描述
给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。
k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
示例 1:
输入:head = [1,2,3,4,5], k = 2 输出:[2,1,4,3,5]
示例 2:
输入:head = [1,2,3,4,5], k = 3 输出:[3,2,1,4,5]
提示:
- 链表中的节点数目为
n 1 <= k <= n <= 50000 <= Node.val <= 1000
进阶:你可以设计一个只用 O(1) 额外内存空间的算法解决此问题吗?
解题思路
分组反转 · 迭代
先数够 k 个,再在组内反转,最后把三截接回去
这道题可以拆成三个动作:数够一组、组内反转、接回前后两截。
用 groupPrev 指向当前组的前一个节点,从它开始向后数 k 步得到 kth(组尾)。如果数不满,说明剩余不足 k 个,直接结束。组尾后面的 groupNext 必须提前保存,因为反转后组尾会变成新的组头,原来的组头则要指向 groupNext。
反转时把 prev 初始化成 groupNext 而不是 null,这样组内反转完成后,最后一节点自然接回后面的链,不会断。
从第 0 个开始数满 3 个:本组是 [2, 7, 4],kth 指向组尾。
- groupPrev
- #0
反转结束后,groupPrev->next 接到新的组头 kth;然后 groupPrev 移到本组原来的第一个节点,它现在是下一组的前驱。
自拟主例两组各 3 个,最后剩 2 个。第二组反转后是 [6,1,9],不是 [1,9,6];这些节点在原链表里的顺序必须逐一看清。
每组只遍历两次(数长度 + 反转),总时间 O(n),额外空间 O(1)。
易错点
- prev 初始化为 null:组尾没有接回 groupNext,后面的链全丢。
- 忘记保存 oldStart:groupPrev 无法移动到下一组的前驱位置。
- 数不满 k 个仍继续反转:最后不足 k 个的节点会被错误翻转。
- groupPrev 直接移到 kth:下一轮会跳过或重复处理节点。
递归 · 每组反转后接剩余
先确认够 k 个,反转本组,再递归接后面
代码更短,但递归深度为 n/k,最坏 O(n) 调用栈;仅作对照。
递归先向后走 k 步:如果走不满,直接返回原 head,不反转。走满后,node 正好是下一组的起点。
接着反转从 head 开始的 k 个节点,反转后的头是 prev、尾是原来的 head。让 head->next 接上“后面部分递归处理”的结果,返回 prev。
它把“接回下一组”交给递归返回值,省掉了 groupPrev 和 oldStart 的维护,但调用栈随组数增长。
- 1向后数 k 个,不足则返回原 head。
- 2反转当前 k 个节点。
- 3原 head 接到递归结果,返回新组头。
易错点
- 数长度时没有保存 groupNext:递归入口会丢。
- 反转后返回 head 而不是 prev:返回的是组尾。
- 不足 k 个时仍然反转:尾部顺序被破坏。
代码与演示
主解法配有图解;对照解法用于比较思路、复杂度或实现方式。
head = [2,7,4,9,1,6,3,8],k = 3dummy 接到 head,groupPrev 从 dummy 开始;每轮先向前数 k 个节点。
查看 C++ 代码
延伸阅读
- 先把“组”看成一个整体,再处理组内反转和组间连接,复杂指针题会清晰很多。
- 本题是两两交换(24 题)的推广;k=2 时就是两两交换。