旋转数组
给定一个数组,将数组中的元素向右移动 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) 的原地算法。
题目让我们至少使用三种方法,我们就使用三种方法吧~
方法一:
还记得我们交换两个int的代码吗?
int temp = a1;
a1 = a2;
a2 = temp;
是这样吧~
我们以nums=[1, 2, 3, 4, 5, 6, 7],k=3为例
第一次变换:1 2 3 1(4) 5 6 7
第二次变换:1 2 3 1(4) 2(5) 6 7
第三次变换:1 2 3 1(4) 2(5) 3(6) 7
第四次变换:1 2 3 1(4) 2(5) 3(6) 4(7)
我们可以看出:我们需要一个k长度的数组存储中间因为1 2 3覆盖掉的4 5 6;可以看出,在第四次变换中,我们的4又重新派上了用场。
代码如下:
public void rotate(int[] nums, int k) {
if (k == nums.length || k == 0 || nums.length == 1)
return ;
k = k % nums.length;
int[] tempArray = new int[k]; //store the temp elements
for (int i = 0; i < k; i++)
tempArray[i] = nums[i];
//init tempArray
int temp = 0;
int index = 0;
int tempIndex = 0;
for (int i = 0; i < nums.length; i++) {
index = (i + k) % nums.length;
temp = nums[index];
nums[index] = tempArray[tempIndex];
tempArray[tempIndex] = temp;
tempIndex = (tempIndex + 1) % k;
}
}
方法二:
在第一种方法中,我们需要不停变换之前的临时数组tempArray中的元素,好的,我不想变。
我要考虑另外的思路。
还是上面的例子nums=[1, 2, 3, 4, 5, 6, 7],k=3
初始状态:1 2 3 4 5 6 7
最终状态:5 6 7 1 2 3 4
是不是发现了些什么?
最后的三个元素移动到了前面,前面的元素移动到了后面
像极了将前面的元素删除之后再插入到后面(在顺序表中)【但是我并没有这样写】
代码如下:
public void rotate3(int[] nums, int k) {
k = k % nums.length;
if (k == 0)
return ;
int[] temp = new int[k];
int index = 0;
for (int i = nums.length - k; i < nums.length ; i ++)
temp[index ++] = nums[i];
//end store elements
for (int i = nums.length - k - 1 ; i >= 0 ; i --)
nums[i+ k] = nums[i];
for (int i = 0 ; i < k ; i ++)
nums[i] = temp[i];
}
方法三:
emm,为什么要利用一个数组存储?不能用一个int的量吗?
可以。
仍然是上面的例子:nums=[1, 2, 3, 4, 5, 6, 7],k=3
我们进行如下的变换:
5(1) 6(2) 7(3) 1(4) 2(5) 3(6) 4(7)
大变换是从nums[0] to nums[3] to nums[6] to nums[2] to nums[5] to nums[1] to nums[4] to nums[0](end)
但是这并不绝对,因为有可能出现nums=[1, 2, 3, 4, 5, 6],k=2的情况,如果还是仅仅从0开始,就会发生如下情况
nums[0] to nums[2] to nums[4] to nums[0](end)
无法完成一次整体的循环
针对这种情况,就要依次从0-k-1作为起点进行k次大的变换
代码如下:
public void rotate2(int[] nums, int k) {
k = k % nums.length;
if (k == 0)
return ;
BitSet bitSet = new BitSet(nums.length);
int prev;
int next;
int nextIndex = 0;
for (int i = 0 ; i < k ; i ++) {
next = nums[i]; //每一次开始起始的值都要发生变化
for (int index = i ; !bitSet.get(index) ; index = nextIndex) {
nextIndex = (index + k) % nums.length; //下一个位置
prev = nums[nextIndex];
nums[nextIndex] = next;
next = prev;
bitSet.set(index); //标记当前位数
}
}
}
bitset用于记录那些值是被替换过的,下一次进行循环的时候就不会重复操作。