碰到仇人,就让他做 K 个一组翻转链表吧
给定链表和正整数 k,从头开始每 k 个节点组成一组:
- 一组恰好有
k个节点,就反转这一组; - 最后一组不足
k个,保持原来的顺序; - 只能改变节点之间的指针,不能交换节点里的值;
- 最好原地完成,也就是额外空间为
O(1)。
例如:
输入:1 → 2 → 3 → 4 → 5 → 6 → 7 → 8,k = 3
输出:3 → 2 → 1 → 6 → 5 → 4 → 7 → 8
前两组各有三个节点,所以分别翻转;最后只剩 7 → 8,不够三个,原样留下。
标准解法的四个角色
group_previous:当前分组前面的节点;kth:当前分组的最后一个节点;group_next:下一组的第一个节点,也是当前组的边界;current和previous:真正执行局部反转的两根工作指针。
哨兵节点 dummy 的价值,是把“第一组”变成普通组。没有它,第一组前面没有节点,每次接回去都要单独写一套逻辑;有了它,每组都只需要做同一件事:找到 kth、翻转、接回、继续。
完整的 Python 写法如下:
class Solution:
def reverseKGroup(self, head, k):
dummy = ListNode(0, head)
group_previous = dummy
while True:
kth = group_previous
for _ in range(k):
kth = kth.next
if kth is None:
return dummy.next
group_next = kth.next
previous = group_next
current = group_previous.next
while current is not group_next:
next_node = current.next
current.next = previous
previous = current
current = next_node
old_group_start = group_previous.next
group_previous.next = kth
group_previous = old_group_start
这里最值得咂摸的一行是 previous = group_next。普通链表翻转通常从 previous = None 开始,但在这道题里,当前组翻转后的尾节点最终本来就应该指向下一组。因此,我们让 previous 一开始就站在 group_next,组内反转完成时,尾巴也顺手接好了。
input · k = 3
指针翻转现场
步骤 1 / 16
先放一个哨兵节点
dummy 站在链表最前面。它不参与翻转,却让第一组和后续分组使用完全相同的接回逻辑。
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8
∅
主链1 → 2 → 3 → 4 → 5 → 6 → 7 → 8
dummy = ListNode(0, head)group_previous = dummywhile True:kth = group_previousfor _ in range(k):kth = kth.nextif kth is None:return dummy.nextgroup_next = kth.nextprevious = group_nextcurrent = group_previous.nextwhile current is not group_next:next_node = current.nextcurrent.next = previousprevious = currentcurrent = next_nodeold_group_start = group_previous.nextgroup_previous.next = kthgroup_previous = old_group_start