给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
进阶:
你可以设计一个只使用常数额外空间的算法来解决此问题吗?
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
首先题目很简单。
而且要求空间复杂度为O(1),这就要求递归是不可以的。
题目分析:
* 1->2->3->456
* 2->1->3->456
* 3->2->1->456
* 4->3->2->1
一看是很简单,但是接着我昨天写的代码继续调试,发现还是报错。
其实我整个理念就没搞懂!就是不完全的代码。
于是,我重新分析:
/** * 1->2->3->456 * 2->1->3->456 * 3->2->1->456 * 4->3->2->1 * 得出结论: 每次都是 与prev节点进行交换 * prev 是 一直都是 1 这个节点 * 2022年3月13日14:56:35 现在到底需要 start 和 prev 几个节点都是一个问题! * 首先 next节点 是肯定要的 如第一步的 2 * 其次 1会指向 3 所以1也需要1个节点 * 那么继续看第二步 再来判断1到底需要用什么指针 * 第二步: next此时指向3了 3 需要 跟 2交换 而此时的2 上面是没有指针指向的 同时1 也需要指向 4 了。 * 所以明确了。 在第二步,3已结有指针了, 而1,2都需要指针。 所以在第一步的时候必须有2个指针指向当时的1和2了 * 假设第一步结束时候是prev指针指向1,start指针指向了2,第二步操作做完:prev指向了1 start指向了3 next指向4 再来看第三步。 * 第三步: 4(cur)的下一个指向 3(start) 1(prev)的下一个指向5(right) 所以回头第二步: * 第二步: 3(cur)的下一个指向 2(start) 1(prev)下一个指向指向4(right) 再回到第一步怎么给prev及 start赋值 * 第一步: 2(cur)的下一个指向1(则是start) 1(prev)的下一个指向3(rigjt) * 所以得出结论: prev和start一开始都指向1 * 再来看遇到下一个k组的逻辑,因为到现在还不需要用虚拟头结点 * 图示改为 k=2即可 * 1->2->3->4 * 2->1->3->4 * 2->1->4->3 2022年3月13日16:46:24 原来这里还有1步 3要指向下一个啊 * 4->3->2->1 * * 第一步: 2(cur)的下一个指向1(则是start) 1(prev)指向3(rigjt) next指向3 同上 * 第二步: 得重新赋值 不然3的下一个会指向2 显然不对了。 * 在第一步结束时候重新赋值: next指向4 prev指向1 start指向3 * 1(prev)的下一个指向 3 * 第二步操作: 4(cur)的下一个指向 3(start) 3(prev指向!) 的下一个指向5(rigjt)(如果有) * start 指向 4(start) * 这里面区别就出来了。 需要有指针去指向上一个组的最后一个节点 * 1(prev)的下一个指向 4(start) 很关键!!! * 最后得出结论: start需要一直赋值,用于next的下一个指向它。 * prev节点也明白了 一直都是每一组的第一个节点了 一直用于指向next的next! * 所以总归 需要 3个 指针 * 最后 初始 弄1个虚拟头结点 (假设)作为上一组的最后一个节点 指向当前(第一组)的头节点 * dummy.cur = head; * 所以最后返回的时候就是dummy.cur 且第一次转换时候 同样做操作 prev等于第一组的头结点 * start 是一直变化的 * * 还是一直有问题啊 2022年3月13日16:20:46 这题真得背下来了。 3个数的时候直接不行了 逻辑还是有问题 * 1->2->3 ->4->5->6 i=0 * 2->1->3 ->4->5->6 i=1 需要变换start * 3->2->1 ->4->5->6 i=2 * 3->2->1 ->5->4->6 i=3 需要转换staret * 3->2->1 ->6->5->4 i=4 * 3->2->1 ->6->5->4 * k个数 只会遍历 k-1次 */
分析内容如上,得出结论是肯定需要不止2个节点。
但同时也发现我越分析越乱,且非常浪费时间。就算用笔记记下来也不行。
其实我就是分析了下到底需要多少个指针。
分析到了:
先从这步开始: 1->2->3 k=3 要变成 2->1->3
- 肯定需要1个指针(叫做指针A吧)来指向当前1个节点,然后每次都会指向它的next。
- 如 1->2->3 k=3 这里面的 2
- 1也会指向3,所以1也需要1个指针
那此时指针就这2个就够了吗?
这一步: 2->1->3 要交换成为: 3->2->1
则操作是: 3的next要指向2
,1的next则由3指向3原先的next
。
3就是上面分析到的指针A当前指向的了,暂且不谈。
思考下这步真的需要多少个指针?
1的next都改变了,所以1必须用个指针指向!
3要指向2,2是一个具体的变量吗?可以用之前指针的next啥的来代替吗?答案是不行。
所以2也必须用个指针在第一步去指向了。
重写第一、二步
那么重写第一步和第二步:
第一步: 1->2->3 要变成 2->1->3
第二步: 2->1->3 要变成: 3->2->1
具体流向:
指针 A: 先是2 , step1后是3,最后是null结束遍历
指针 B: 先没有,step1后是2,step2后未知 因为上面null结束遍历了
指针 C: 先没有,step1后是1,step2种指向3原先的next
看下加了第三步后逻辑有没有变化,同时更改名字 k=4
第一步: 1->2->3 ->4 要变成 2->1->3 ->4
第二步: 2->1->3->4 要变成: 3->2->1 ->4
第三步: 3->2->1 ->4 要变成: 4->3->2->1
具体流向:
指针 target(每轮交换后放到第一位的节点的指向):
先是2 , step1后是3,最后是null结束遍历
指针 targetNext
(每轮开始时target的下一个指向,用于保留)
先是3 , step1后是4,step2后是null
指针 prev
: (实际上始终是第一个节点)
先没有,step1后是1,step2后指向不变,但改变它的next指针的指向。
step3后依旧它的next指向,且依旧是prev.next=targetNext。但step3后prev
指针是否改变真的未知。
指针 curFirst
每轮结束时候的第一个节点)
先没有,step1后是2,step2后是3,step3后由于结束遍历则未知
带着上面未知的疑问,进行下一步思考。
先放上面的代码:
/** 每一组的第一个节点 */
ListNode start = head;
/** 每一组的依次遍历的节点 从每一组的第二个节点开始直到最后一个节点 */
ListNode cur = start;
//还得需要1个变量 不然高不上去
while (start != null) {
ListNode target = cur.next;
ListNode targetNext = target.next;
target.next = start;
cur.next =targetNext;
start = target;
if (targetNext == null) {
break;
}
}
return start;
如果要求不是每一个都反转,而是指定前K个反转,怎么做?
所以就需要上面未知的代码了。
先放代码: 其实只是改动跳出的逻辑。
这让我思考了 要不要优化到while里面呢。
/** 每一组的第一个节点 */
ListNode start = head;
/** 每一组的依次遍历的节点 从每一组的第二个节点开始直到最后一个节点 */
ListNode cur = start;
//还得需要1个变量 不然高不上去
while (start != null) {
ListNode target = cur.next;
ListNode targetNext = target.next;
target.next = start;
cur.next =targetNext;
start = target;
if (targetNext == null || i == k-1) {
break;
}
}
return start;
再然后,如果k个为1组,每组组内反转,这就是这道题目了!
- 2022年3月13日21:38:30
记录下今天怼这题的心路历程吧,希望也能起到写一篇博客彻底记住的效果。
- 2022年3月13日22:42:38
靠,看评论区反转链表,有个那么复杂有啥用呢。