K 个一组翻转链表 图解题解
每 K 个节点翻一组,最后不足的保留原样——原地改指针,不借额外空间。
K 个一组翻转链表,就像把书架上的书每 K 本一段倒着重插:先从当前位置往后数 K 本确认够一组(数到 null 就不够、这段不动),够了就把这 K 个节点的 next 指针逐个掉头反转,再把前段的末尾接到新组头、原组头的 next 接到下一组起点,然后移到下一段重复。不够 K 本的末尾保持原样不碰。
这道题到底在问什么
- 输入
- 1→2→…→9, k=3
- 输出
- 3→2→1→6→5→4→9→8→7
最优解:一步一步想明白
- 3下面逐组、逐条边演示——每翻一根箭头 2 帧,看清「为什么先存 next 再掉头」。
- 4链表 1→2→3→4→5→6→7→8→9,k=3。先看第 1 组 [1,2,3]。
- 5第 1 组 [1,2,3]:先从组头走 3 步确认凑齐 3 个(kth 落在组尾 3)。局部 prev=null,cur 指组头 1,准备逐条反转。
- 6组内第 1 根:cur=1,prev=null。先用 next 抓住 2——一旦把 1 的箭头掉头就找不到后面了,「先记下一个」是命根。
- 7cur.next = …:先把 1 原来指向 2 的箭头断开(图上变 ·)。因为已用 next 记住了 2,断了也不怕丢。
- 8cur.next = prev:1 的箭头掉成指向 null(组内最左,反转完再接上一组)(图上变 ←)。prev 进到 1、cur 进到 2。
- 9组内第 2 根:cur=2,prev=1。先用 next 抓住 3——一旦把 2 的箭头掉头就找不到后面了,「先记下一个」是命根。
- 10cur.next = …:先把 2 原来指向 3 的箭头断开(图上变 ·)。因为已用 next 记住了 3,断了也不怕丢。
- 11cur.next = prev:2 的箭头掉成指向 1(图上变 ←)。prev 进到 2、cur 进到 3。
- 12第 1 组反转完成:组内变成 3→2→1(已翻好,标绿)。新头是 3、新尾是 1。
- 13组间连接:上一组反转后的尾 1 要指向下一组反转后的头。代码靠保存的「上组尾」与本组反转返回的新头对接,然后处理下一组。
- 14第 2 组 [4,5,6]:先从组头走 3 步确认凑齐 3 个(kth 落在组尾 6)。局部 prev=null,cur 指组头 4,准备逐条反转。
- 15组内第 1 根:cur=4,prev=null。先用 next 抓住 5——一旦把 4 的箭头掉头就找不到后面了,「先记下一个」是命根。
- 16cur.next = …:先把 4 原来指向 5 的箭头断开(图上变 ·)。因为已用 next 记住了 5,断了也不怕丢。
- 17cur.next = prev:4 的箭头掉成指向 null(组内最左,反转完再接上一组)(图上变 ←)。prev 进到 4、cur 进到 5。
- 18组内第 2 根:cur=5,prev=4。先用 next 抓住 6——一旦把 5 的箭头掉头就找不到后面了,「先记下一个」是命根。
- 19cur.next = …:先把 5 原来指向 6 的箭头断开(图上变 ·)。因为已用 next 记住了 6,断了也不怕丢。
- 20cur.next = prev:5 的箭头掉成指向 4(图上变 ←)。prev 进到 5、cur 进到 6。
- 21第 2 组反转完成:组内变成 6→5→4(已翻好,标绿)。新头是 6、新尾是 4。
- 22组间连接:上一组反转后的尾 4 要指向下一组反转后的头。代码靠保存的「上组尾」与本组反转返回的新头对接,然后处理下一组。
- 23第 3 组 [7,8,9]:先从组头走 3 步确认凑齐 3 个(kth 落在组尾 9)。局部 prev=null,cur 指组头 7,准备逐条反转。
- 24组内第 1 根:cur=7,prev=null。先用 next 抓住 8——一旦把 7 的箭头掉头就找不到后面了,「先记下一个」是命根。
- 25cur.next = …:先把 7 原来指向 8 的箭头断开(图上变 ·)。因为已用 next 记住了 8,断了也不怕丢。
- 26cur.next = prev:7 的箭头掉成指向 null(组内最左,反转完再接上一组)(图上变 ←)。prev 进到 7、cur 进到 8。
- 27组内第 2 根:cur=8,prev=7。先用 next 抓住 9——一旦把 8 的箭头掉头就找不到后面了,「先记下一个」是命根。
- 28cur.next = …:先把 8 原来指向 9 的箭头断开(图上变 ·)。因为已用 next 记住了 9,断了也不怕丢。
- 29cur.next = prev:8 的箭头掉成指向 7(图上变 ←)。prev 进到 8、cur 进到 9。
- 30第 3 组反转完成:组内变成 9→8→7(已翻好,标绿)。新头是 9、新尾是 7。
- 31所有满 3 组都已就地反转、组间接好。最终链表:3→2→1→6→5→4→9→8→7。(若结尾有不足 3 个的一组,保持原顺序不动。)
⚠️ 容易写错的地方
✗ 错:没数够 k 就反转
✓ 对:先走 k 步确认够 k 个,不够整组不动
题目要求不足 k 的尾段保持原顺序
✗ 错:组内反转前不存 next
✓ 对:必须先 nxt = cur.next 再 cur.next = prev
掉头会覆盖 cur.next,后半段丢失
✗ 错:反转完忘了接两端
✓ 对:保存上组尾,反转后 groupPrev.next = 新头、再更新 groupPrev
组与组不接好链表就断成几截
完整代码(Python / C++ / Java)
Python
def reverseKGroup(head, k):
dummy = ListNode(0, head)
groupPrev = dummy
while True:
kth = groupPrev
for _ in range(k): # 数够 k 个
kth = kth.next
if not kth:
return dummy.next # 不足 k,结束
groupNext = kth.next
prev, cur = groupNext, groupPrev.next
while cur != groupNext: # 组内三指针反转
nxt = cur.next
cur.next = prev
prev = cur
cur = nxt
tmp = groupPrev.next # 接好两端
groupPrev.next = kth
groupPrev = tmpC++
ListNode* reverseKGroup(ListNode* head, int k){
ListNode dummy(0); dummy.next = head;
ListNode* groupPrev = &dummy;
while (true) {
ListNode* kth = groupPrev;
for (int i = 0; i < k && kth; i++) kth = kth->next;
if (!kth) return dummy.next; // 不足 k
ListNode* groupNext = kth->next;
ListNode *prev = groupNext, *cur = groupPrev->next;
while (cur != groupNext) { // 组内反转
ListNode* nxt = cur->next;
cur->next = prev; prev = cur; cur = nxt;
}
ListNode* tmp = groupPrev->next;
groupPrev->next = kth; groupPrev = tmp;
}
}Java
public ListNode reverseKGroup(ListNode head, int k){
ListNode dummy = new ListNode(0); dummy.next = head;
ListNode groupPrev = dummy;
while (true) {
ListNode kth = groupPrev;
for (int i = 0; i < k && kth != null; i++) kth = kth.next;
if (kth == null) return dummy.next; // 不足 k
ListNode groupNext = kth.next;
ListNode prev = groupNext, cur = groupPrev.next;
while (cur != groupNext) { // 组内反转
ListNode nxt = cur.next;
cur.next = prev; prev = cur; cur = nxt;
}
ListNode tmp = groupPrev.next;
groupPrev.next = kth; groupPrev = tmp;
}复杂度
时间
O(n)
数 k 个 + 反转,每个节点被访问常数次
空间
O(1)
只用 groupPrev/kth/prev/cur/next 几个指针,原地反转
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 K 个一组翻转链表 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
不用 dummy 节点行不行?+
行,但第一组反转后头会变,需单独处理「新头」并作返回值;dummy 让第一组和后续组用同一套「groupPrev 接线」逻辑,代码更统一。
能否递归实现?+
能:每次反转前 k 个,递归反转后面,再把本组尾接到递归返回的头。逻辑更短但有 O(n/k) 递归栈,迭代版是 O(1) 空间。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄把 K 个一组翻转链表 一步步讲透(全站已上线 905 份讲透,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
把这道题真正拿下
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。