文章目录
问题描述
LeetCode 25. K 个一组翻转链表
给定一个链表的头节点 head 和一个整数 k,要求每 k 个节点一组进行翻转。如果剩余节点不足 k 个,则保持原有顺序。返回翻转后的链表头节点。
示例:
输入:head = [1,2,3,4,5], k = 2
输出:[2,1,4,3,5]
核心思路
- 分组处理 :将链表按
k个节点分组,每组内部翻转。 - 虚拟头节点:引入虚拟头节点简化边界处理(如原头节点被翻转的情况)。
- 指针定位 :使用两个指针
pre和end定位每组的前驱节点和末尾节点。 - 断链与连接:翻转后需正确连接前一组尾节点与当前组头节点,以及当前组尾节点与下一组头节点。
详细实现步骤
1. 初始化虚拟头节点
- 创建
dummy节点,指向原链表头节点,避免处理头节点翻转的特殊情况。
2. 指针定位分组
pre:始终指向当前待翻转分组的前驱节点。end:遍历链表,定位当前分组的末尾节点。
3. 分组翻转逻辑
- 移动
end指针 :向后移动k次找到当前分组的末尾。 - 断链:将当前分组与后续节点断开。
- 翻转当前分组 :调用
reverse()函数翻转当前分组。 - 重新连接:将翻转后的头节点连接到前驱节点,翻转后的尾节点连接到下一组头节点。
- 更新指针 :将
pre和end移动到下一组的前驱节点位置。
4. 翻转函数 reverse()
- 使用迭代法翻转链表,返回新头节点。
完整代码实现
java
class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode pre = dummy; // 当前组的前驱节点
ListNode end = dummy; // 当前组的末尾节点
while (end.next != null) {
// 1. 定位当前组的末尾
for (int i = 0; i < k && end != null; i++) {
end = end.next;
}
if (end == null) break; // 剩余节点不足k个,直接结束
// 2. 断链并翻转
ListNode start = pre.next; // 当前组的起始节点
ListNode nextGroup = end.next; // 下一组的头节点
end.next = null; // 断开当前组与后续节点的连接
// 3. 翻转当前组,并重新连接链表
pre.next = reverse(start); // 翻转后的头节点连接到前驱
start.next = nextGroup; // 原组的头(翻转后的尾)连接到下一组
// 4. 移动pre和end到下一组的前驱位置
pre = start;
end = pre;
}
return dummy.next;
}
// 迭代法翻转链表
private ListNode reverse(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while (curr != null) {
ListNode next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
}
}
代码解析
-
虚拟头节点
dummy- 作用:避免处理头节点被翻转的特殊情况。
- 初始化
dummy.next = head,最终返回dummy.next。
-
指针移动与分组定位
end指针移动k步找到当前组的末尾。若中途遇到null,说明剩余节点不足k个,直接结束循环。
-
断链与翻转
- 保存当前组的起始节点
start和下一组的头节点nextGroup。 - 断开当前组与后续节点的连接:
end.next = null。 - 调用
reverse(start)翻转当前组,并将翻转后的头节点连接到前驱节点pre。
- 保存当前组的起始节点
-
重新连接链表
- 翻转后的尾节点(原
start)需要连接到下一组的头节点:start.next = nextGroup。 - 更新
pre和end到下一组的前驱位置(即原start的位置)。
- 翻转后的尾节点(原
复杂度分析
- 时间复杂度 :O(n)
每个节点最多被遍历两次(一次分组定位,一次翻转)。 - 空间复杂度 :O(1)
仅使用常数级别的额外指针变量。
示例演示
以输入 head = [1,2,3,4,5], k = 2 为例:
- 分组
[1,2]→ 翻转为[2,1],连接后链表变为[2,1,3,4,5]。 - 分组
[3,4]→ 翻转为[4,3],连接后链表变为[2,1,4,3,5]。 - 剩余
[5]不足k,保持原顺序。
最终输出:[2,1,4,3,5]。
总结
通过虚拟头节点、指针定位和分组断链,本方案高效实现了链表的每 k 个节点翻转。代码逻辑清晰,时间复杂度为线性,适合处理大规模链表数据。