题目
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
示例 :
给定这个链表:1->2->3->4->5
当 k = 2 时,应当返回: 2->1->4->3->5
当 k = 3 时,应当返回: 3->2->1->4->5
说明 :
你的算法只能使用常数的额外空间。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
心得
写这道题目可以用递归,但对递归不太了解,所以明天打算写一下递归的专题,暂时不在这道题上耗时间了,等递归用的顺手的时候再回来写它。
学到的点:
1、计算时间复杂度,虽然我在一个大循环里面包了两个不同的小循环,但是时间复杂度加在一起,一共是 f ( 3 l − l k ) f(3l-\frac{l}{k}) f(3l−kl),并不是我想的会复杂度很高。
2、要理清楚ListNode
的存储结构,不然容易出现指向地址的值产生循环的情况。
思路
注:里面pre
和corr
默认使用0->xxxxxx
如此,虽然画的是直接指向需要转换的链表的头指针,但是他们默认是.next
指向。
我没太看仔细看网上的思路,跟着自己的想法写了这个,用语言不知道要怎么表达,干脆放图好了:
在上图中,先考虑只改变ListNode
链表首的翻转变动。
k
=
1
k = 1
k=1的时候链表不做改动,
k
=
2
k = 2
k=2的时候两两交换,
k
=
3
k = 3
k=3的时候就把
k
=
2
k=2
k=2时候翻转好的链表作为一个整体,再和指向的第三个链表做一次两两交换,
k
=
4
k=4
k=4就可以把
k
=
3
k=3
k=3的已经转换好的链表看做一个整体,再与指向的第四个链表做一次两两交换,以此类推。
这样就可以发现可以根据 k k k数量的做一次循环了,这个循环的复杂度只有 f ( k ) f(k) f(k),需要的指针如下所示:
指针end
永远都是跟在second
的后面,指向链表尾部,second
指向end
的前一个的原因就是上面说的将前一部分看做一个整体,和后面指向的数字进行两两交换,first->second
就是那个整体,end
是那个后面指向的数字。所以first
永远都是指在要交换的链表头(就是比如图上1->2->3->4
时候,
k
=
3
k=3
k=3,第一次要对1->2->3
交换,那first
指向链表中存储1
的位置,当前面三个交换完之后,对下一组进行交换,那么first
就应该指向4
,因为它是下一组的链表之首。)
pre.next
这个指针我用来存储头节点,到最后可以通过输出它,而输出已经被翻转好的链表。
但是,因为end
指针会一直和first
指针进行交换,所以pre.next
指针指向的地址只会一直跟着first
,而无法将转换好的链表完整输出:
所以我加了一个corr
指针去跟踪这个翻转后的完整的链表,让corr
指向翻转好的链表地址。
这样,就得到正确的结果了。
为了知道有多少段链表需要我去进行翻转,而不用临时判空等操作浪费时间,所以在用循环知道了链表会有多长之后,整除 k k k,知道了有多少段链表需要翻转,然后再整合上面的思路。
优化
emm我觉得递归的话可能效率更高?因为时间都浪费在让occr
这个指针去跟踪更新的链表了。