链表——K个一组翻转链表
给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。
k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
1.整体思路
- 用虚拟头节点避免边界问题
- 每k个节点截成一段
- 翻转这一段
- 把翻转后的段重新接回原链表
- 不足k个就停止
2.关键变量
- pre:当前要翻转组的前一个节点
- start:当前组第一个节点
- end:当前组最后一个节点
- next:下一组的第一个节点
3.执行流程
1.找到k个节点
2.切断与后面的连接
3.翻转这k个节点
4.重新连接前后链表
5.继续下一组
示例演示
输入:1->2->3->4->5,k=2
输出:2->1->4->3->5
执行过程:
第一组:1,2 → 翻转 → 2,1
第二组:3,4 → 翻转 →4,3
剩余:5 → 不翻转
最终:2->1->4->3->5
完整代码实现如下:
public 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) {
// 1. 定义虚拟头节点,方便处理头部翻转
ListNode dummy = new ListNode(0);
dummy.next = head;
// pre 指向每组的前驱节点,end 指向每组的最后一个节点
ListNode pre = dummy;
ListNode end = dummy;
// 2. 循环:直到无法再取出 k 个节点
while (end.next != null) {
// 让 end 向后走 k 步,找到当前组的末尾
for (int i = 0; i < k && end != null; i++) {
end = end.next;
}
// 如果 end == null,说明剩余节点不足 k 个,直接退出
if (end == null) break;
// 3. 截取要翻转的片段
ListNode start = pre.next; // 本组要翻转的第一个节点
ListNode next = end.next; // 保存下一组的头节点
// 4. 断开本组与下一组的连接,翻转本组链表
end.next = null;
pre.next = reverse(start); // 翻转后,pre 指向新头
// 5. 把翻转后的链表与后面的链表接上
start.next = next;
// 6. 更新 pre 和 end,准备处理下一组
pre = start;
end = pre;
}
return dummy.next;
}
// 翻转单组链表(普通翻转)
private ListNode reverse(ListNode head) {
ListNode pre = null;
ListNode cur = head;
while (cur != null) {
ListNode next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
return pre;
}
}
要注意java原生的ListNode链表类本身没有内置的reverse()方法,所以必须自己手动实现翻转逻辑,不能直接调用。
为什么不能直接用?
Java 标准库中:
-
Collections.reverse() 只能翻转 List 集合(如 ArrayList、LinkedList),不能直接作用于自定义的链表节点 ListNode
-
你写的是自定义单向链表,JDK 没有提供现成的翻转方法,必须自己写翻转逻辑
注意:
Java 里可以直接写 null,而且必须小写!
Java 没有 NULL、没有 nullptr,只有 null。
Java vs C++ 对比
- C++
- NULL → 宏,本质是 0
- nullptr → C++11 空指针(真正的空指针类型)
- 不能写 null(会报错)
- Java
- 只有 null(小写)
- 表示空引用
- 没有大写 NULL
- 没有 nullptr
Java中main方法必须写在类内
为什么 return dummy.next 是正确的?
dummy的唯一作用就是固定链表的入口,dummy.next永远指向整个链表的真正头节点
一开始:
ListNode pre = dummy;
pre.next = reverse(start);
所以:
- pre就是dummy
- pre.next = 新头
等价于
dummy.next = 新头
也就是说:
翻转后的新头节点,直接挂在了dummy后面
浙公网安备 33010602011771号