LeetCode 25困难链表 · 分组反转
K 个一组翻转链表 图解题解
每 K 个节点翻一组,最后不足的保留原样——原地改指针,不借额外空间。
K 个一组翻转链表,就像把书架上的书每 K 本一段倒着重插:先从当前位置往后数 K 本确认够一组(数到 null 就不够、这段不动),够了就把这 K 个节点的 next 指针逐个掉头反转,再把前段的末尾接到新组头、原组头的 next 接到下一组起点,然后移到下一段重复。不够 K 本的末尾保持原样不碰。
这道题到底在问什么
每 k 个节点一组翻转,不足 k 个保持原样。
- 输入
- 1→2→3→4→5, k=3
- 输出
- 3→2→1→4→5(尾段 4,5 不足 3 不动)
最优解:一步一步想明白
- 3关键不是背模板,而是看懂为什么这题适合链表 · 分组反转。
- 4规则:从头开始每 k=3 个节点一组,就地反转;最后不足 k 个的那组保持原样。
- 5从组头往后数 k 步:1→2→3 正好凑满一组,可以反转;数的时候若碰到 null 就说明不足 k、这组不动。
- 6关键帧:把组内 next 指针逐个掉头 —— 变成 1←2←3,新组头是 3、原头 1 变成组尾。这组先和后面断开(·)。
- 7接回:前驱指向新头 3,原头 1 的 next 指向下一组起点 4。group_prev 移到这组组尾 1,准备处理下一组。
- 8下一组从 4 开始往后数:只有 4、5 两个,不足 k=3 → 整组保持原样,不反转。
- 9最终结果:3→2→1→4→5。第一组反转、尾段不足 k 保留。
- 12把这句话记住,下次遇到同类题,就能更快选出方向。
⚠️ 容易写错的地方
✗ 错:不足 k 个也翻转
✓ 对:先数够 k 个,不够就保持原样
题目明确尾段不足 k 不动
✗ 错:反转后忘了接后继
✓ 对:原头.next 要指向下一组起点
只接了新头、漏接后继,会把后半段链表整段丢掉。
✗ 错:变量名和动画不一致
✓ 对:代码变量沿用动画里的核心名字
学习时最怕脑内维护两套概念
完整代码(Python / C++ / Java)
Python
class Solution:
def reverseKGroup(self, head, k):
dummy = ListNode(0, head)
group_prev = dummy
while True:
kth = group_prev
for _ in range(k):
kth = kth.next
if not kth:
return dummy.next
group_next = kth.next
prev, cur = group_next, group_prev.next
while cur != group_next:
tmp = cur.next
cur.next = prev
prev = cur
cur = tmp
tmp = group_prev.next
group_prev.next = kth
group_prev = tmpC++
class Solution {
public:
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; i++) {
kth = kth->next;
if (!kth) return dummy.next;
}
ListNode* groupNext = kth->next;
ListNode* prev = groupNext, *cur = groupPrev->next;
while (cur != groupNext) {
ListNode* tmp = cur->next;
cur->next = prev;
prev = cur;
cur = tmp;
}
ListNode* tmp = groupPrev->next;
groupPrev->next = kth;
groupPrev = tmp;
}
}
};Java
class Solution {
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; i++) {
kth = kth.next;
if (kth == null) return dummy.next;
}
ListNode groupNext = kth.next;
ListNode prev = groupNext, cur = groupPrev.next;
while (cur != groupNext) {
ListNode tmp = cur.next;
cur.next = prev;
prev = cur;
cur = tmp;
}
ListNode tmp = groupPrev.next;
groupPrev.next = kth;
groupPrev = tmp;
}
}
}套路模板 · 链表 · 分组反转
# 链表分组反转骨架(迭代,O(1) 空间)
dummy = ListNode(0, head); group_prev = dummy
while True:
kth = group_prev
for _ in range(k): # 从 group_prev 走 k 步
kth = kth.next
if not kth: return dummy.next # 不足 k,整段保持原样,结束
group_next = kth.next
prev, cur = group_next, group_prev.next
while cur != group_next: # 组内逐个掉头
cur.next, prev, cur = prev, cur, cur.next
tmp = group_prev.next # 原头,将成为下一组的 group_prev
group_prev.next = kth # 前驱接新头
group_prev = tmp复杂度
时间复杂度
O(n)
每个节点恰好被「数一次 + 反转一次」,总共 n 个节点
空间复杂度
O(1)
只用 dummy 和 groupPrev/kth/prev/cur 几个指针,迭代法无递归栈,与 n、k 无关
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 K 个一组翻转链表 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
怎么判断凑满 K 个?+
从组起点往后走 K 步,走得到说明够;碰到 null 说明不足 K、这组不翻。
翻转一组的指针怎么接?+
用 dummy + 记每组 prev:翻转后 prev.next 接新头、组的原头接下一组、prev 前移到原头。
能 O(1) 空间吗?+
能,迭代逐组翻转只用几个指针;递归版是 O(n/k) 栈。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。