Description
Given an array, rotate the array to the right by k steps, where k is non-negative.
Examples
1、
Input: [1,2,3,4,5,6,7] and k = 3
Output: [5,6,7,1,2,3,4]
Explanation:
rotate 1 steps to the right: [7,1,2,3,4,5,6]
rotate 2 steps to the right: [6,7,1,2,3,4,5]
rotate 3 steps to the right: [5,6,7,1,2,3,4]
2、
Input: [-1,-100,3,99] and k = 2
Output: [3,99,-1,-100]
Explanation:
rotate 1 steps to the right: [99,-1,-100,3]
rotate 2 steps to the right: [3,99,-1,-100]
Notes
思路一:想到了时间O(n)空间O(1)的做法,就是按照隔k个数两两交换,等到数组n个元素都做过交换时,就交换完毕。但有一个细节想了很久,就是当我们完成一轮交换后,若未完成整个过程,那么下一轮交换的该从哪里开始?例如,我们如果在第一个元素开始循环,等第一轮循环结束,此时还没交换完n个元素,那我们应该开始第二轮交换,但第二轮交换从哪个位置开始呢?若从第二个元素开始第二轮循环,会不会产生第二个元素在第一轮已经被交换过了的情况呢?若第二个元素已在第一轮被后移k,则第二轮从第二个元素开始必定会产生错误。
答案是:第二轮一定从第二个元素开始。为什么呢?因为如果在第一轮中回到过第二个元素,那么肯定存在一个循环模式,使得若干次后,当前位置相对于起点(第一个元素),后移了一个位置,那么将这个模式继续下去,则一定会经过第三第四…个位置,那么交换就在第一轮完成了,所以会产生矛盾。因此若在第一轮未完成交换,那么,第二个元素肯定没被后移过。同理,若第二轮未完成交换第三个元素肯定没被后移过…那么总共需要几轮循环呢?应该是数组长度len和后移步长k的最大公约数(自己推的不一定对,而且在代码中并不需要这个结论)。
思路二是看题解别人的思路,用三次对称交换,很巧妙。复杂度应该和第一种一样。
Codes
思路一
class Solution {
public:
void rotate(vector<int>& nums, int k) {
int len=nums.size();
if(!len) return;
k=k%len;
if(!k) return;
int cnt=0;
for(int i=0;i<k;++i)
{
int tempindex=i;
int temp=nums[i];
while((tempindex+k)%len!=i)
{
tempindex=(tempindex+k)%len;
int t=nums[tempindex];
nums[tempindex]=temp;
temp=t;
cnt++;
}
nums[i]=temp;
cnt++;
if(cnt>=len) break;
}
return;
}
};
思路二,利用三次对称变换。
class Solution {
public:
void reverse(vector<int>& nums,int left,int right)
{
int mid=(right+left)/2;
for(int i=left;i<=mid;++i)
{
int temp=nums[i];
nums[i]=nums[right-(i-left)];
nums[right-(i-left)]=temp;
}
return;
}
void rotate(vector<int>& nums, int k) {
int len=nums.size();
if(!len) return;
k=k%len;
if(!k) return;
reverse(nums,0,len-k-1);
reverse(nums,len-k,len-1);
reverse(nums,0,len-1);
return;
}
};