LeetCode k 个为一组进行翻转
题目需要将单链表以 k 个为一组进行翻转的情况。这个问题在实际的编程中也很常见,解决这个问题可以提高我们的链表操作技能。
本文将分享如何实现单链表的 k 个一组翻转以及相关的知识点,带给大家更深入、更系统的链表操作理解。
题目描述
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
示例
输入:1->2->3->4->5
当 k = 2 时,输出:2->1->4->3->5
当 k = 3 时,输出:3->2->1->4->5
解题思路
这个问题可以转化为把每 k 个结点分为一组,然后对每一组进行翻转。
做题常用的方法是:先定义一个哑结点,然后将其 next 指针指向头结点。这样可以有效减少很多边界情况的判断,同时对于头结点的操作可以和对于其他结点的操作一致。
我们可以每次找到待反转的 k 个结点的位置,然后反转这 k 个结点。但是这个过程中要注意的是边界条件的判断,也就是当翻转完最后一组结点的时候,需要把剩余的结点串起来。通过哑结点,这个过程可以简化很多。
在具体实现过程中我们可以定义如下几个变量:
- pre:每一组开始前的结点,也就是上一组翻转后的结点。
- end:每一组结束后的结点,也就是这一组翻转后的最后一个结点。
- start:每一组开始前的结点,也就是这一组翻转后的第一个结点。
- next:下一组开始前的结点,也就是这一组翻转后的最后一个结点的下一个结点。
具体的操作步骤:
-
首先判断链表是否为空或者 k 是否为 1,如果是则直接返回头结点。
-
定义虚拟的头结点dummyHead,将其 next 指针指向链表的头结点head。
-
定义 pre、start、end 的初始位置,即 pre = dummyHead,start 和 end 都指向 head。
-
根据图片中描述的顺序,翻转 start 到 end 之间的结点。
-
将翻转完的这组结点连接起来,操作如下:
- pre 的 next 指针指向 end,即上一组翻转后的结点指向这一组翻转后的最后一个结点。
- start 结点指向下一组翻转的开始结点,即 end 的下一个结点。
- 完成连接后,将 pre 指向 start 即可,这样就完成了一组的翻转操作。
-
处理剩余的部分,将剩下的未处理的结点连接到最终链表的末尾。
-
返回 dummyHead 的下一个结点即为结果。
代码实现
下面是用 Java 语言实现的代码,我们可以通过测试数据来验证我们的算法是否正确。
public class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
if (head == null || head.next == null || k == 1) {
return head;
}
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode pre = dummyHead, start = head, end = head;
int count = 0;
while (end != null) {
count++;
end = end.next;
if (count == k && end != null) {
ListNode next = end.next;
end.next = null;
pre.next = reverseList(start);
start.next = next;
pre = start;
start = next;
end = next;
count = 0;
} else if (end == null && count == k) {
pre.next = reverseList(start);
}
}
return dummyHead.next;
}
private ListNode reverseList(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode pre = null;
ListNode curr = head;
while (curr != null) {
ListNode next = curr.next;
curr.next = pre;
pre = curr;
curr = next;
}
return pre;
}
}
测试数据验证
我们可以使用测试数据来验证一下我们的算法是否正确。
public class Test {
public static void main(String[] args) {
// 初始化链表
ListNode head = new ListNode(1);
ListNode second = new ListNode(2);
ListNode third = new ListNode(3);
ListNode forth = new ListNode(4);
ListNode fifth = new ListNode(5);
head.next = second;
second.next = third;
third.next = forth;
forth.next = fifth;
Solution solution = new Solution();
// 验证 k = 2 的情况
int k1 = 2;
ListNode res1 = solution.reverseKGroup(head, k1);
ListNode.printList(res1); // 预期输出:2->1->4->3->5
// 验证 k = 3 的情况
int k2 = 3;
ListNode res2 = solution.reverseKGroup(head, k2);
ListNode.printList(res2); // 预期输出:3->2->1->4->5
}
}
通过测试数据的输出可以看出,我们的算法已经正确的翻转了每 k 个结点一组的单链表。
总结
在解决题目的过程中,我们掌握了如何实现单链表的 k 个一组翻转,掌握了相关的代码实现技巧和解题思路。
针对链表相关的题目,我们应该养成使用哑结点的好习惯,这样可以有效减少很多边界情况的判断。对于单链表的基本操作,我们应该熟练掌握,如单链表的创建、增加、删除、查找等操作。
希望本文能对你的学习有所帮助,使你的链表相关的算法更加得心应手。