文章目录
- 前言
- 链表反转|K个一组翻转链表
- 解题方法:
- 头插法处理:
- 穿针引线法处理:
- 总结
前言
提示:没有人天生就喜欢一种气味而讨厌另一种气味。文明的暗示而已。
链表反转|K个一组翻转链表
给你链表的头节点 head
,每 k
个节点一组进行翻转,请你返回修改后的链表。
k
是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k
的整数倍,那么请将最后剩余的节点保持原有顺序。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
进阶:你可以设计一个只用 O(1)
额外内存空间的算法解决此问题吗?
思路来的很快,重点是代码层面的编写,这个很重要,思路呢?就是常见的头插法和穿针引线法来处理数组反转问题,当然今天我们也是采用这两种方法解决的,那就开始实现他吧😃
解题方法:
头插法处理:
头插法重点在理解虚拟节点上,如果这个问题解决了,相比较而言要比穿针引线要好实现的多😄, 我们试着把链表整体分为3段,一段是已经翻转的,一端是正在反转,一端是未反转的。为了方便翻转,我们需要京链表遍历一边,统计一下链表的长度len,然后将链表进行分组n=len/k,接下来就是循环进行分组翻转链表。 我们尝试这画一些图,能够更有里的说明:
结假设我们开始翻转 4 节点:
具体思路:
- 找到链表的长度 len 分组
- cur.next = cur.next.next; next.next = pre.next; pre.next = next;
- 循环下一次
上代码😄
/**
* 方法2:头插法
*
* @param head
* @param k
* @return
*/
public static ListNode reverseKGroup2(ListNode head, int k) {
ListNode dummyNode = new ListNode(-1);
dummyNode.next = head;
ListNode cur = head;
int len = 0;
while (cur != null) {
cur = cur.next;
len++;
}
int n = len / k; // 计算出来分几组
ListNode pre = dummyNode;
cur = head;
for(int i = 0; i < n; i++) {
for(int j = 0; j < k - 1; j++) { // ? k - 1 画图就知道了
ListNode next = cur.next;
cur.next = cur.next.next;
next.next = pre.next;
pre.next = next;
}
pre = cur;
cur = cur.next; // 记得修改指针
}
return dummyNode.next;
}
穿针引线法处理:
这个思路可以回顾一下穿针引线,到底是怎么回事🤣
首先还是将链表分组翻转, 我们就可以一组一组的处理,把他们分成已经翻转的、正在翻转的和未翻转的三部分,同时为了方便处理头节点,我们使用了一个虚拟的节点。
接下来就是遍历,根据k个为一组找到四个关键位置,并使用变量per,start,end,next标记,比如下面的图:
接着我们就开始翻转,对应颜色进行翻转,我们将end.next = null ,直接使用链表翻转,复用链表翻转的常规操作。🤖注意指针的变化,head便是传入方法的参数,我们接着看图:
当然翻转之后,我们接下来就是将原始链表缝起来,这就需要调整指针域,同样这里也要注意指针的变化🤖
接着调整指针进行下一次循环:
总结一下📝
- 遍历找到四个关键位置,复用常规翻转链表
- pre.next = end; start.next = next; 调整指针域
- 调整下一次循环
了解上面的图,代码应该也会写吧🥰
/**
* 方法1: 穿针引线法
*
* @param head
* @param k
* @return
*/
public static ListNode reverseKGroup(ListNode head, int k) {
ListNode dummyNode = new ListNode(-1);
dummyNode.next = head;
ListNode pre = dummyNode;
ListNode end = dummyNode;
while (end.next != null) { // 最终结束的地方
for (int i = 0; i < k && end != null; i++) {
end = end.next; // 找到当前分组的最后一个
}
if (end == null) {
break;
}
// 找到start next
ListNode start = pre.next;
ListNode next = end.next;
end.next = null; // 思考?
pre.next = reverse(start);
start.next = next;
pre = end;
// 调整下一次循环
end = pre;
}
return dummyNode.next;
}
复习一下链表反转💕:
private static 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;
}
总结
注意:指针域的变化,多画图更容易理解,链表反转重点,重点,重点!!!