题目
给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。
示例1:
输入:head = [1,2,3,4,5], k = 2
输出:[4,5,1,2,3]
示例2:
输入:head = [0,1,2], k = 4
输出:[2,0,1]
代码
代码1:
public ListNode rotateRight(ListNode head, int k) {
if (head == null||head.next==null) {
return head;
}
ListNode curNext = head.next;
while (curNext.next != null) {
curNext = curNext.next;
}
curNext.next = head;
for (int i = 0; i < k; i++) {
ListNode cur = head;
while (cur.next != curNext) {
cur = cur.next;
}
head = curNext;
curNext = cur;
}
curNext.next = null;
return head;
}
代码2:
public ListNode rotateRight(ListNode head, int k) {
if (head == null || head.next == null) {
return head;
}
int count = 1;
ListNode cur = head;
while (cur.next != null) {
cur = cur.next;
count++;
}
cur.next = head;
for (int i = 0; i < count - (k % count); i++) {
cur = cur.next;
head = head.next;
}
cur.next = null;
return head;
}
解题思路
我们可以发现这种题目节点的个数,val没有发生改变,改变的只是连接的位置,而且是相邻节点之间依次发生变化,因此我们可以将首尾相连,然后返回移动之后的头节点,并将环链表变为单链表。这是我们的大体思路。我们先看第一种解法。
代码1:
首先我们实例化curNext这个节点,这个节点先遍历到最后一个节点,然后将curNext的next改为head,这样单链表就形成了环。像这样:
这时候,进入循环,实例化一个节点cur,遍历链表,直到cur是curNext的前驱节点也就是“0x456”,这个位置。然后让head指向curNext,让curNext指向cur。像这样:
这样就完成了一次的向右移动,循环k次(假设k为2),跳出循环后,将curNext的next赋值为null(将环变成单链表),最后返回head节点,问题解决。
但是如果我们将代码1提交到LeetCode中去,会发现报出时间超时,这是因为时间复杂度大了,我们可以看到这种解题,再循环中还需要遍历链表,找出curNext的前驱节点,然后再往前移,这样就导致时间复杂度为O(n2),我们再来看代码2的解题思路。
代码2:
代码1中我们可以看到head和curNext是往后移动,这样就导致要找到前驱节点。那么能不能让head往前移动呢?当然可以,但是往前移动就不单单是k次循环,是与链表的长度有关。
假设链表长度count为5。
当k为1时,head需要移动4次,也就是循环4次。
当k为2时,head需要移动3次,也就是循环3次。
……
当k为4时,head需要移动1次,也就是循环1次。
得到结论:循环次数 = count - k。
如果是这样,那么我们又掉进了坑里面了,为什么?我们k的取值都是小于5的,到那时我们知道k是可以大于5的,如果大于5,那么循环次数就为负数了,这显然不可能,那么我们发现以5作为一个周期,如果大于5,只要k对5取余,然后再用链表长度count减去余数就可以解决了。所以最后得出:循环次数 = count -(k%count)(k<=5也是成立的)。
得到了循环次数,那么在一次循环中,我们只要让head往前移动一个节点,让cur再前移动一个节点,最后跳出循环,将cur.next赋值为null,返回head,问题解决。代码2就减少了遍历链表的操作,这样时间复杂度也相应的变成了O(N)。
好了,这次的每日一题分享就到这里,如果对你有所帮助,欢迎点赞收藏,发现问题或者提出建议,欢迎各位私信,谢谢大家。最后附上今日题目链接。
附:LeetCode61题——旋转链表