← 返回题目列表

如何 K 个一组反转链表?

高频 困难 第 16 / 28 题 更新于 2026/07/29
链表K个一组反转反转链表指针

简化版

K 个一组反转链表的核心是先确认后面够不够 K 个节点,够就反转这一段,不够就保持原样。通常用 dummy 维护总头,用 groupPrev 指向当前组前驱,反转后把前驱、组尾和下一组接回去。

详细版

流程可以拆成 4 步:找到当前组第 K 个节点 kth;记录下一组头 groupNext = kth.next;反转 [groupPrev.next, groupNext) 这段;把 groupPrev.next 接到反转后的新头,把旧组头接到 groupNext,再移动 groupPrev 到旧组头。

dummy -> 1 -> 2 -> 3 -> 4 -> 5, k=2
after:  dummy -> 2 -> 1 -> 4 -> 3 -> 5

这题难点不在反转本身,而在边界:不足 K 个不能反转,反转后组与组之间要接好。

完整版教学

一、K 组反转比普通反转多了分组边界

普通反转只需要把整条链表翻过来。K 个一组反转则要求每 K 个节点反转一次,最后不足 K 个保持原顺序。

input:  1 -> 2 -> 3 -> 4 -> 5
k = 2
output: 2 -> 1 -> 4 -> 3 -> 5

这里节点 5 不足一组,所以不动。这个“不足 K 个不反转”的条件是最容易漏的边界。

记忆钩子:先数够 K 个,再动手反转;没数够就收工。

二、用 dummy 和 groupPrev 稳住连接点

反转第一组会改变头节点,所以需要 dummy。groupPrev 表示当前组的前驱。

dummy -> 1 -> 2 -> 3 -> 4 -> 5
  ^
groupPrev

每轮要做的是反转 groupPrev.next 开始的 K 个节点。反转后,groupPrev.next 要指向这一组的新头。

如果没有 groupPrev,你会很难把上一组和当前组接起来。

三、先找到 kth 和 groupNext

groupPrev 出发往后走 K 步,找到当前组第 K 个节点。

groupPrev -> 1 -> 2 -> 3
k=2, kth=2, groupNext=3

如果走不到 K 步,说明剩余节点不足一组,直接返回 dummy.next

ListNode kth = groupPrev;
for (int i = 0; i < k && kth != null; i++) {
  kth = kth.next;
}
if (kth == null) break;
ListNode groupNext = kth.next;

groupNext 必须提前保存,否则反转过程中 kth.next 会被改掉。

四、反转半开区间 [start, groupNext)

当前组起点是 groupPrev.next,终点是不包含的 groupNext。可以用半开区间思维:

start = groupPrev.next
reverse until cur == groupNext

代码:

ListNode prev = groupNext;
ListNode cur = groupPrev.next;
while (cur != groupNext) {
  ListNode next = cur.next;
  cur.next = prev;
  prev = cur;
  cur = next;
}

为什么 prev 初始化为 groupNext?因为反转后旧组头会变成组尾,它的 next 应该接到下一组头。

五、反转后重新接回总链表

反转前的组头会变成反转后的组尾,要先保存。

ListNode oldHead = groupPrev.next;
// reverse ...
groupPrev.next = kth;
groupPrev = oldHead;

1 -> 2 -> 3 -> 4、k=2 为例:

before group:
groupPrev -> 1 -> 2 -> groupNext(3)

after reverse:
groupPrev -> 2 -> 1 -> 3
                  ^
              next groupPrev

然后下一轮从旧组头,也就是新组尾继续。

六、复杂度和易错点

每个节点最多被访问和反转常数次,所以时间 O(n)。只用几个指针变量,额外空间 O(1)。

容易错的点有 4 个:

1. 不足 K 个却反转了
2. 没保存 groupNext
3. 反转后 groupPrev.next 接错
4. groupPrev 没移动到旧组头

写这题时建议先画一组 k=2,再画一组 k=3。只在脑子里转指针,很容易绕进去。

七、常见误区与追问

  • 误区:剩多少节点都要反转。 题目通常要求不足 K 个保持原样,要先确认够 K 个。
  • 误区:反转后 kth 还是原来的尾节点。 在当前组反转后,原 kth 会成为新头。
  • 误区:不用保存 groupNext 也能接回。 反转会改指针,不提前保存很容易丢失下一段。
  • 追问:为什么 prev 初始为 groupNext? 这样旧组头反转后自然指向下一组,少一次额外接线。
  • 追问:复杂度是多少? 时间 O(n),额外空间 O(1),每个节点只处理常数次。
  • 追问:k=1 怎么办? 每组一个节点,链表不变,代码也应自然返回原链表。

八、加强记忆

K 个一组反转记成“数 K、断边界、反半开、接回去”。先从 groupPrev 找到 kth,不够 K 个就停;保存 groupNext,反转 [groupPrev.next, groupNext);反转后 groupPrev.next 指向新头,groupPrev 移到旧组头。dummy 稳住总头,半开区间稳住边界。