1. 题目链接
2. 题目描述
给你链表的头节点 head
,每 k
个节点一组进行翻转,请你返回修改后的链表。
k
是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k
的整数倍,那么请将最后剩余的节点保持原有顺序。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
3. 题目示例
示例 1 :
输入:head = [1,2,3,4,5], k = 2
输出:[2,1,4,3,5]
示例 2 :
输入:head = [1,2,3,4,5], k = 3
输出:[3,2,1,4,5]
4. 解题思路
- 问题描述:给定一个链表和整数k,要求每k个节点为一组进行反转,不足k个的保持原样。
- 核心思路:
- 统计节点数:先遍历链表获取总长度,确定可以分成多少完整的k组。
- 分组处理:使用外层循环处理每组k个节点,内层循环实现k个节点的反转。
- 链表连接:反转后需要将子链表正确连接到原链表中:
- 反转前的第一个节点(现在是尾节点)指向下一组的头节点
- 前驱节点指向反转后的新头节点
- 指针更新:处理完一组后,更新前驱指针到下一组的前驱位置。
- 关键点:
- 虚拟头节点:统一处理头节点可能被反转的情况。
- 三指针法反转:使用pre、cur、nxt三个指针实现局部反转。
- 连接处理:特别注意反转后子链表与原链表的连接关系。
5. 题解代码
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
// 统计链表总节点数n
int n = 0;
for (ListNode cur = head; cur != null; cur = cur.next) {
n++;
}
// 创建虚拟头节点dummy,指向head,简化头节点处理
ListNode dummy = new ListNode(0, head);
// p0表示当前待处理组的前驱节点,初始为dummy
ListNode p0 = dummy;
// pre用于反转链表,初始为null
ListNode pre = null;
// cur表示当前处理的节点,初始为head
ListNode cur = head;
// 当剩余节点数>=k时,继续处理
for (; n >= k; n -= k) {
// 反转当前k个节点(同LeetCode 92题方法)
for (int i = 0; i < k; i++) {
ListNode nxt = cur.next; // 保存下一个节点
cur.next = pre; // 反转当前节点的指针
pre = cur; // pre前移
cur = nxt; // cur前移
}
// 将反转后的子链表接入原链表
ListNode nxt = p0.next; // 保存原p0.next(反转前的第一个节点)
p0.next.next = cur; // 反转后的尾节点指向下一组的头节点
p0.next = pre; // 前驱节点指向反转后的头节点
p0 = nxt; // 更新p0为下一组的前驱节点
}
return dummy.next;
}
}
6. 复杂度分析
- 时间复杂度:O(n)
- 统计节点数:O(n)
- 反转操作:每个节点被处理两次(一次统计,一次反转),总体O(2n) → O(n)
- 空间复杂度:O(1)
- 只使用了常数个额外指针变量