LeetCode 25:K 个一组翻转链表
题目思路
这道题要求每 k 个节点翻转一次,如果最后剩余节点不足 k 个,则保持原顺序。


我的第一反应不是直接操作链表指针,而是利用 Deque(双端队列)暂存当前这一组节点。
核心思路:
-
先从当前
head开始,最多取k个节点放入Deque。 -
如果不足
k个,说明已经到了最后一组,直接返回原链表。 -
如果刚好取到
k个:- 通过
pop()依次取出节点; - 因为前面使用的是
push(),所以取出的顺序正好是原来的逆序; - 将这些节点重新连接起来。
- 通过
-
当前这一组处理完之后,对剩余链表
flag递归执行相同操作。
代码如下:
java
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
Deque<ListNode> deque = new ArrayDeque<>();
ListNode flag = head;
int num = 0;
// 先收集 k 个节点
while (flag != null && num < k) {
deque.push(flag);
flag = flag.next;
num++;
}
// 当前节点数量不足 k,保持原顺序
if (num == k) {
ListNode newHead = deque.pop();
head = newHead;
// 按逆序重新连接
for (int i = 2; i <= k; i++) {
newHead.next = deque.pop();
newHead = newHead.next;
}
// 当前组处理完成,递归处理后面的节点
newHead.next = reverseKGroup(flag, k);
} else {
return head;
}
return head;
}
}
最关键的地方:为什么 push() + pop() 就能实现翻转?
假设:
text
1 -> 2 -> 3
依次执行:
java
deque.push(1);
deque.push(2);
deque.push(3);
队列内部相当于:
text
3
2
1
之后依次:
java
deque.pop();
得到:
text
3 -> 2 -> 1
所以这里实际上是借助 Deque 的**栈特性(后进先出,LIFO)**完成了当前 k 个节点的逆序。
flag 的作用
这里:
java
ListNode flag = head;
flag 相当于一个"探路指针"。
收集节点时:
java
while(flag != null && num < k){
deque.push(flag);
flag = flag.next;
num++;
}
当循环结束后,如果:
text
1 -> 2 -> 3 -> 4 -> 5
↑
flag
假设 k = 3,那么当前组是:
text
1 -> 2 -> 3
而 flag 已经指向:
text
4 -> 5
所以当前组翻转完成后:
java
newHead.next = reverseKGroup(flag, k);
实际上就是:
text
3 -> 2 -> 1 -> 4 -> 5
↑
递归处理
为什么不足 k 个时直接返回?
这是题目的要求:
如果最后剩余的节点不足
k个,就保持原顺序。
例如:
text
1 -> 2 -> 3 -> 4 -> 5
k = 3
第一次处理:
text
1 -> 2 -> 3
翻转成:
text
3 -> 2 -> 1
剩下:
text
4 -> 5
只有 2 个节点,不满足 k = 3,因此:
java
if(num == k){
...
}else{
return head;
}
直接返回:
text
4 -> 5
最终:
text
3 -> 2 -> 1 -> 4 -> 5
这道题的收获
这道题我采用的是:
Deque + 递归
而不是直接进行复杂的链表指针翻转。
这种写法的优点是思路比较直观:先把一组节点"存起来",再按照逆序重新连接。
需要注意的是,这种方法会额外使用 O(k) 的空间。
如果追求这道题的最优空间复杂度,可以进一步学习纯链表指针操作 的写法,把额外空间降低到 O(1)。不过从理解和实现难度来说,目前这个版本已经能够很好地完成题目要求。
时间复杂度为 O(n),每个节点都会被处理一次。