题目描述
思路解析动画文字版
下面逐组、逐条边演示——每翻一根箭头 2 帧,看清「为什么先存 next 再掉头」。
链表 1→2→3→4→5→6→7→8→9,k=3。先看第 1 组 [1,2,3]。
第 1 组 [1,2,3]:先从组头走 3 步确认凑齐 3 个(kth 落在组尾 3)。局部 prev=null,cur 指组头 1,准备逐条反转。
组内第 1 根:cur=1,prev=null。先用 next 抓住 2——一旦把 1 的箭头掉头就找不到后面了,「先记下一个」是命根。
cur.next = …:先把 1 原来指向 2 的箭头断开(图上变 ·)。因为已用 next 记住了 2,断了也不怕丢。
cur.next = prev:1 的箭头掉成指向 null(组内最左,反转完再接上一组)(图上变 ←)。prev 进到 1、cur 进到 2。
组内第 2 根:cur=2,prev=1。先用 next 抓住 3——一旦把 2 的箭头掉头就找不到后面了,「先记下一个」是命根。
cur.next = …:先把 2 原来指向 3 的箭头断开(图上变 ·)。因为已用 next 记住了 3,断了也不怕丢。
cur.next = prev:2 的箭头掉成指向 1(图上变 ←)。prev 进到 2、cur 进到 3。
第 1 组反转完成:组内变成 3→2→1(已翻好,标绿)。新头是 3、新尾是 1。
组间连接:上一组反转后的尾 1 要指向下一组反转后的头。代码靠保存的「上组尾」与本组反转返回的新头对接,然后处理下一组。
第 2 组 [4,5,6]:先从组头走 3 步确认凑齐 3 个(kth 落在组尾 6)。局部 prev=null,cur 指组头 4,准备逐条反转。
组内第 1 根:cur=4,prev=null。先用 next 抓住 5——一旦把 4 的箭头掉头就找不到后面了,「先记下一个」是命根。
cur.next = …:先把 4 原来指向 5 的箭头断开(图上变 ·)。因为已用 next 记住了 5,断了也不怕丢。
cur.next = prev:4 的箭头掉成指向 null(组内最左,反转完再接上一组)(图上变 ←)。prev 进到 4、cur 进到 5。
组内第 2 根:cur=5,prev=4。先用 next 抓住 6——一旦把 5 的箭头掉头就找不到后面了,「先记下一个」是命根。
cur.next = …:先把 5 原来指向 6 的箭头断开(图上变 ·)。因为已用 next 记住了 6,断了也不怕丢。
cur.next = prev:5 的箭头掉成指向 4(图上变 ←)。prev 进到 5、cur 进到 6。
第 2 组反转完成:组内变成 6→5→4(已翻好,标绿)。新头是 6、新尾是 4。
组间连接:上一组反转后的尾 4 要指向下一组反转后的头。代码靠保存的「上组尾」与本组反转返回的新头对接,然后处理下一组。
第 3 组 [7,8,9]:先从组头走 3 步确认凑齐 3 个(kth 落在组尾 9)。局部 prev=null,cur 指组头 7,准备逐条反转。
组内第 1 根:cur=7,prev=null。先用 next 抓住 8——一旦把 7 的箭头掉头就找不到后面了,「先记下一个」是命根。
cur.next = …:先把 7 原来指向 8 的箭头断开(图上变 ·)。因为已用 next 记住了 8,断了也不怕丢。
cur.next = prev:7 的箭头掉成指向 null(组内最左,反转完再接上一组)(图上变 ←)。prev 进到 7、cur 进到 8。
组内第 2 根:cur=8,prev=7。先用 next 抓住 9——一旦把 8 的箭头掉头就找不到后面了,「先记下一个」是命根。
cur.next = …:先把 8 原来指向 9 的箭头断开(图上变 ·)。因为已用 next 记住了 9,断了也不怕丢。
cur.next = prev:8 的箭头掉成指向 7(图上变 ←)。prev 进到 8、cur 进到 9。
第 3 组反转完成:组内变成 9→8→7(已翻好,标绿)。新头是 9、新尾是 7。
所有满 3 组都已就地反转、组间接好。最终链表:3→2→1→6→5→4→9→8→7。(若结尾有不足 3 个的一组,保持原顺序不动。)
三类边界:k=1 / 太短 / 整除,代码的「数够 k 才反转」天然覆盖。
两个高频追问:dummy 的作用、递归 vs 迭代的空间权衡。
参考代码
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 = tmp复杂度
- 时间:O(n),数 k 个 + 反转,每个节点被访问常数次
- 空间:O(1),只用 groupPrev/kth/prev/cur/next 几个指针,原地反转
易错点
面试追问把动画讲成自己的话
追问不用 dummy 节点行不行?
追问能否递归实现?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
相交链表
LeetCode 160 · 简单 · 沿着 链表 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题