旋转数组
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
示例 1:
输入: [1,2,3,4,5,6,7] 和 k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步: [6,7,1,2,3,4,5]
向右旋转 3 步: [5,6,7,1,2,3,4]
示例 2:
输入: [-1,-100,3,99] 和 k = 2
输出: [3,99,-1,-100]
解释:
向右旋转 1 步: [99,-1,-100,3]
向右旋转 2 步: [3,99,-1,-100]
说明:
尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
要求使用空间复杂度为 O(1) 的 原地 算法。
解:
第一个方法就是直接暴力平移
第二个方法是通过画图想到的,将移动前数组和移动后数组进行比较,
[1,2,3,4,5,6,7] k = 3
[5,6,7,1,2,3,4]
会发现其实我们可以从第一位数开始,将它移动到“正确的位置”,例如我们将1移动到4的位置,即将nums[0]移动到nums[0+k],即nums[3],然后将nums[3]同样向后移动k位(越界时区余数),这样移动数组长度的次数后,就将所有数字都放在了“正确的位置”,循环结束
但此时你会发现如果是
k=3
[1,2,3,4,5,6]
[4,5,6,1,2,3]
在此时(nums.length可被k整除),你试着移动两次之后发现,移动数回到了nums[0],首尾发生连接但并没有移动完,但此时继续移动也不对,因为他们在前两次已经移动到正确的位置了,
因此设置flag进行判断,若发生整除循环,则向后错一位,我们从nums[1]再次开始移动。
(想画图来着,手绘板没带回家,以后补图吧=-=)
java:
class Solution {
public void rotate(int[] nums, int k) {
// //1.先暴力平移,整体向右移动一步,执行k次
// int j = 0;
// int len =nums.length;
// if(nums.length>0){
// while(j
// int temp = nums[len-1];
// for(int i=len-1;i>0;i--){
// nums[i]=nums[i-1];
// }
// nums[0]=temp;
// j++;
// }
// }
//2.k步错位
int j=0;
int temp =0;
int temp2=nums[0];
//flag作用是判断当前数组是否k整除并即将开始循环
int flag=0;
for(int i=0;i<nums.length;i++){
int ja=(j+k)%nums.length;
temp=temp2;
//如果开始循环(也就是说先前遍历标记ja,已经到达了曾经走过的开头但是并没有完成length次的交换,那么ja+1向后错一位,避免陷入整除循环)
if(ja==flag){
temp2=nums[(ja+1)%nums.length];
nums[ja]=temp;
j=(ja+1)%nums.length;
flag++;
//数组长度不被k整除,一次完整遍历即可结束
}else{
temp2=nums[ja];
nums[ja]=temp;
j=ja;
}
}
}
}