Leetcode中的array的rotate问题
Leetcode当中有一系列的题目是关于array的,而关于array的问题中又有一些题目是关于array的“rotate”的操作的。准备写一个关于rotate的专题,将关于rotate的问题整理一下。
闲言少叙,我们直接来看第一题。
https://leetcode.com/problems/rotate-array/
原题
Rotate an array of n elements to the right by k steps.
For example, with n = 7 and k = 3, the array [1,2,3,4,5,6,7] is rotated to [5,6,7,1,2,3,4].
Hint:
Could you do it in-place with O(1) extra space?
翻译:将长度为n的数组向右旋转k步,比如n=7,k=3,那么数组 [1,2,3,4,5,6,7] 被旋转成[5,6,7,1,2,3,4]。
提示:
你能在O(1)的额外空间中解决这个问题吗。
问题分析
看到这个问题的基本想法就是每次rotate一步,然后将rotate进行k次就OK了,O(1)也没有什么问题,但是很显然时间复杂度达到O(kn),效率太低啦。其实接下来想的问题就是是否可以仅仅使用swap操作,将元素和他应该到的位置进行swap。
[1,2,3,4,5,6,7] 被旋转成[5,6,7,1,2,3,4],我将1放到4的位置,4放到7的位置,7放到3的位置,3放到6的位置,6放到2的位置,2放到5的位置,最后5放到1的位置,每次我只要计算出这个被替换掉的元素下一次应该到的位置就行了!看似很完美,额外空间复杂度O(1)。
实现了一下发现一个问题,只有k%n和n互质的时候,这个方法才有效!
比如数组为[1,2,3,4],k=2。第一次1换到3,然后3换到1,到了初始位置了,结束,但是数组为[3,2,1,4]。当n和k%n有公因子的时候,就会出现这种问题。这个问题可以这样解决,找到n和k%n的最大公因子c,然后对数组的前c个数进行上述的迭代替换操作。这种方法的时间复杂度是O(n)。
贴上代码供大家参考:
public void rotate(int[] nums, int k)
{
if(nums.length == 0) return;
k = k%nums.length;
if(k < 1) return;
int gcd = divisor(nums.length,k);
for(int i = 0; i < gcd; i++)
{
int temp = nums[i];
int tempIndex = i;
while((tempIndex + k)%nums.length != i)
{
int theTemp = nums[(tempIndex + k)%nums.length];
nums[(tempIndex + k)%nums.length] = temp;
temp = theTemp;
tempIndex = (tempIndex + k)%nums.length;
}
nums[i] = temp;
}
return;
}
int divisor(int m,int n)
{
int temp;
while(m%n!=0){
temp=n;
n=m%n;
m=temp;
}
return n;
}
当然咯,网上通用的做法是先reverse整个数组,然后对前k个和后面n-k个进行reverse,想法很好,但是值得注意的是相比我的方法,该方法可以让每个元素替换两次才到位的哟,其实它的时间复杂度是O(2n),但是这个方法省却了最大公约数的计算,也贴上代码供大家参考。
public void rotate(int[] nums, int k)
{
if(nums.length == 0)
{
return;
}
k = k%nums.length;
reverse(nums,0,nums.length - 1);
reverse(nums,0, k - 1);
reverse(nums,k,nums.length - 1);
return;
}
public void reverse(int[] nums, int head, int end)
{
int temp;
while(end > head)
{
temp = nums[end];
nums[end] = nums[head];
nums[head] = temp;
++head;
--end;
}
return;
}
接下来,我们何妨看看链表的rotate操作,哈哈~~~