题目:25. K 个一组翻转链表 - 力扣(LeetCode)
/**
* Definition for singly-linked list.
* 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) {
ListNode hair = new ListNode(0);
hair.next = head;
ListNode pre = hair;
while (head != null) {
ListNode tail = pre;
// 查看剩余部分长度是否大于等于 k
for (int i = 0; i < k; ++i) {
tail = tail.next;
if (tail == null) {
return hair.next;
}
}
ListNode nex = tail.next;
ListNode[] reverse = myReverse(head, tail);
head = reverse[0];
tail = reverse[1];
// 把子链表重新接回原链表
pre.next = head;
tail.next = nex;
pre = tail;
head = tail.next;
}
return hair.next;
}
public ListNode[] myReverse(ListNode head, ListNode tail) {
ListNode prev = tail.next;
ListNode p = head;
while (prev != tail) {
ListNode nex = p.next;
p.next = prev;
prev = p;
p = nex;
}
return new ListNode[]{tail, head};
}
}
比较难理解的是myReverse()部分
**reverseKGroup 方法**
这个方法接收两个参数,一个是 ListNode 类型的 head,代表链表的头结点;另一个是整数类型 k,表示我们要反转连续 k 个节点组成的子链表的数量。该方法返回反转后的链表头结点。
以下是 reverseKGroup 方法的详细步骤:
1. 创建一个新的虚拟头结点 hair,其 next 指针指向原链表的头结点 head。同时定义一个预置结点 pre,初始时也指向虚拟头结点 hair。
2. 使用 while 循环遍历链表,直到找到满足条件的子链表,或者遍历完整个链表。
3. 在 while 循环内,首先定义一个尾结点 tail,并使用 for 循环查看剩余部分长度是否大于等于 k。如果小于 k,则直接返回反转后的头结点。
4. 如果满足反转条件,则保存 tail 结点的后继结点 nex,并调用 myReverse 方法反转从 head 到 tail 的子链表。反转后得到的新头结点和尾结点分别存储在 reverse 数组的第 0 个和第 1 个元素中。
5. 更新原链表结构,将子链表反转后的头结点接到 pre 结点后面,尾结点接到 nex 结点前面。同时更新 pre 和 head 的值,使它们分别指向反转后的尾结点和尾结点的后继结点。
6. 当 while 循环结束后,返回 hair 结点的后继结点作为反转后的链表头结点。
**myReverse 方法**
这个方法接收两个参数,一个是 ListNode 类型的 head,代表待反转子链表的头结点;另一个是 ListNode 类型的 tail,代表待反转子链表的尾结点。该方法返回一个包含两个元素的 ListNode 数组,数组中的第 0 个元素是反转后的尾结点,第 1 个元素是反转后的头结点。
以下是 myReverse 方法的详细步骤:
1. 初始化一个 prev 结点,其值为 tail 结点的后继结点。同时定义一个指针 p,初始时指向 head 结点。
2. 使用 while 循环反转子链表,直到 prev 结点等于 tail 结点。在循环内,首先保存 p 结点的后继结点 nex,然后修改 p 结点的 next 指针使其指向 prev 结点。接着将 prev 和 p 结点向前移动一步,直至完成子链表的反转。
3. 完成反转后,返回一个包含反转后尾结点和头结点的 ListNode 数组。
通过以上两个方法的组合,我们可以实现在单向链表中反转连续 k 个节点组成的子链表的功能。
`myReverse` 方法负责反转一段连续的链表。该方法接收两个参数:`ListNode head` 和 `ListNode tail`。`head` 表示要反转的子链表的起始节点,`tail` 表示要反转的子链表的终止节点。该方法返回一个包含两个 `ListNode` 类型的数组,其中第一个元素是反转后的子链表的尾节点,第二个元素是反转后的子链表的头节点。
以下是详细的解释:
1. 首先,初始化一个 `ListNode` 类型的变量 `prev`,并将它的值设置为 `tail` 的下一个节点。这样做是因为反转链表的过程实际上是将节点原本的 `next` 指针改为指向前一个节点。因此,我们需要记录当前节点之前的节点。
2. 然后,定义一个 `ListNode` 类型的变量 `p`,并将它的值设置为 `head`。`p` 变量将在循环过程中逐次递增,表示当前正在处理的节点。
3. 接下来,进入一个 `while` 循环。只要 `prev` 不等于 `tail`(也就是还没到达反转子链表的末尾),就会一直执行循环内的操作。循环内的主要操作如下:
- 先保存 `p` 节点的下一个节点到一个临时变量 `nex`。
- 然后将 `p` 节点的 `next` 指针设置为 `prev`,这是反转链表的关键步骤,改变了链表的前后顺序。
- 然后将 `prev` 设置为 `p`,即将 `p` 节点视为新的前驱节点。
- 最后将 `p` 设置为 `nex`,准备处理下一个节点。
4. 当循环结束后,说明整个子链表都已经反转完毕。这时,`prev` 已经变成了反转后的子链表的尾节点,`p` 变量则指向反转后的子链表的最后一个节点(也是原子链表的首个节点)。因此,可以将 `prev` 存储到结果数组的第一个元素,将 `p` 存储到结果数组的第二个元素。
5. 最后,返回包含反转后子链表尾节点和头节点的数组。
总之,`myReverse` 方法实现了对一段连续链表的反转操作,并返回了反转后子链表的尾节点和头节点。
while (prev != tail) {
ListNode nex = p.next;
p.next = prev;
prev = p;
p = nex;
}
反转链表,传递的tail就是新链表的起点,起点的下一个节点就是目的地(记作prev),起点记作p,while()循环上面是初始化的起点和终点的值,它是不断变化的,那么while()循环里面要更新p , prev, (nex 是下一个起点)