Given a list, rotate the list to the right by k places, where k is non-negative.
For example:
Given 1->2->3->4->5->NULL and k = 2,
return 4->5->1->2->3->NULL.
这个题刚开始一直看不懂题意,不明白旋转是个什么意思。其实就是按照给定的k值将倒数k个节点移动到前面,而且这个k值大于链表总节点的时候从第一个再次开始计算。也就是题中的例子,当k是2的时候结果如此,当k的数值满足k%5=2的时候都会是这个结果。
那么明白了题意之后就会发现这道题挺简单的,只需要找到倒数第k(k对总长度取模计算)个节点就好。找到这个位置有两个思路,第一种就是先遍历一遍链表得到该链表的总长度len,然后使用len减去k得到的数值就是我们需要遍历多少个位置,需要注意的是这里我们需要根据计算出来的数值遍历节点,而很可能这个数值非常大,比如说给定:1->2->3 ,k的值为200000000,那么计算出来len为3,需要移动2000000000-3次,这样会运行超时,所以需要进行取模运算得到2,只需要遍历两次,而且无论len-k是正是负取模运算都适合。然后找到需要断开的位置之后将其断开, 并且需要将链表的尾节点和头节点连接起来。
class Solution {
public:
ListNode* rotateRight(ListNode* head, int k) {
if(!head) {
return head;
}
int len = 1;
ListNode* temp = head;
while(temp->next) {//计算出总共有多少个节点
temp = temp->next;
len++;
}
temp->next = head;//将此时最后一个节点与第一个节点连接起来,然后可以直接从当前的节点进行遍历
k = len - k;//计算出需要遍历多少个节点
while(k%len) {//这里使用取模计算
temp = temp->next;
k--;
}
ListNode* newHead = temp->next;//新节点
temp->next = NULL;//断开
return newHead;
}
};
第二种方法是使用两个指针,第一个指针先移动k次,然后第二个指针从头开始移动,当第一个指针到达为尾节点的时候,第二个指针刚好移动到总长度减去K的位置了,这种方法比较简单。但是k得值同样可能会很大,所以第一个指针移动的时候并不一定要移动k次,期间需要进行取模判断来缩小移动的次数。
class Solution {
public:
ListNode* rotateRight(ListNode* head, int k) {
if(!head) {
return head;
}
ListNode* temp1,*temp2;
temp1 = temp2 = head;
int num = 0;
while(num!=k) {//将temp1移动
num++;
if(!temp1->next) {//此时计算出链表的长度,并且更新k的值
temp1 = head;
k = k%num;//计算出余数
num = 0;
} else {
temp1 = temp1->next;
}
}
if(temp1 == temp2) {
return temp1;
} else {
while(temp1->next) {//将temp2移动到需要断开的位置
temp1 = temp1->next;
temp2 = temp2->next;
}
ListNode* newHead = temp2->next;
temp1->next = head;
temp2->next = nullptr;
return newHead;
}
}
};