LeetCode刷题笔记—双指针第189题:旋转数组
189:旋转数组(难度:中等)
本题要求三种写法
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。 进阶: 尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。 你可以使用空间复杂度为 O(1) 的 原地 算法解决这个问题吗? 示例 1: 输入: nums = [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: 输入:nums = [-1,-100,3,99], k = 2 输出:[3,99,-1,-100] 解释: 向右旋转 1 步: [99,-1,-100,3] 向右旋转 2 步: [3,99,-1,-100] 提示: 1 <= nums.length <= 2 * 104 -231 <= nums[i] <= 231 - 1 0 <= k <= 105
写法1:使用额外的数组
即另外开辟一个新数组,按照规则把源数组的元素按照移动后的位置放到新数组中
代码:
class Solution { public void rotate(int[] nums, int k) { int n = nums.length; int[] newArr = new int[nums.length]; for(int i = 0;i<nums.length;i++){ newArr[(i + k) % n] = nums[i]; } System.arraycopy(newArr, 0, nums, 0, nums.length); } }
写法2:环装替代(难)
现有[1,2,3,4,5]这个数组,k = 3,定义一个temp来临时存储元素,这样就不需要开辟一个新数组
1 2 3 1 5 temp = 4
1 4 3 1 5 temp = 2
1 4 3 1 2 temp = 5
1 4 5 1 2 temp = 3
3 4 5 1 2 完成,共交换了5次,即交换次数 = 数组长度
但是若有[1,2,3,4]这个数组,k = 2,使用此方法:
1 2 1 4 temp = 3
3 2 1 4 temp = 1
3 2 1 4 temp = 1
1 2 1 4 temp = 3
1 2 3 4 temp = 1
会发现一直在重复对调1和3,陷入死循环,那么这个时候就需要在陷入循环的时候让“指针”向前移动一位,来完成剩下的部分
我们发现,是否陷入循环是由数组元素个数和k的最大公因数决定的,第一个例子5和3的最大公因数是1,第二个例子4和2的最
大公因数是2,即最大公因数是奇数那么不会陷入循环,反之则会陷入循环,那么我们可以引入一个count = gcd(nums.length,k),
具体请看代码:
class Solution { public void rotate(int[] nums, int k) { int n = nums.length; k = k % n; int count = gcd(k, n); for (int start = 0; start < count; ++start) { int current = start; int prev = nums[start]; do { int next = (current + k) % n; int temp = nums[next]; nums[next] = prev; prev = temp; current = next; } while (start != current);
/*若最大公因数为奇数,那么当start == current时,这时已经完成了一次全部交换,之后退出循环,start++,因为进行了count次整体交换,又因为count是奇数,
所以最后还会得到正确数组:
如:1 2 3 4 5 6 k = 3 ,count = 3
↑ 第一次交换
4 5 6 1 2 3 start = 1所以这次循环从nums[1]开始
↑ 第二次交换
1 2 3 4 5 6 start = 2 所以这次循环从nums[2]开始
↑ 第三次交换
4 5 6 1 2 3 所以最后仍是正确答案
若最大公因数为偶数,那么当start == current时,这时已经完成了一次全部交换,之后退出循环,start++,因为进行了count次整体交换,又因为count是偶
数,++start就会起到让“指针”向前移动一位的作用。
所以最后还会得到正确数组:
如:1 2 3 4 k = 2,count = 2
↑ 第一次交换
3 2 1 4 start = 1所以这次循环从nums[1]开始
↑ 第二次交换
3 4 1 2 所以最后仍是正确答案
*/
}
} public int gcd(int x, int y) { return y > 0 ? gcd(y, x % y) : x; } }
写法3:数组翻转
这种做法就是将一个数组翻转,然后从第k位分开,nums[0]~nums[k mod n - 1 ]的数进行翻转,nums[k mod n]到nums[n - 1]进行翻转,然后将两个数组翻转连接在一起
如[1,2,3,4,5] , k = 2
5 4 3 2 1
分开:
5 4 | 3 2 1
翻转:
4 5 | 1 2 3 得到正确数组
class Solution { public void rotate(int[] nums, int k) { k = k % nums.length; reverse(nums, 0, nums.length - 1); reverse(nums, 0, k - 1); reverse(nums, k, nums.length - 1); } public void reverse(int[] nums, int start, int end) { while (start < end) { int temp = nums[end]; nums[end] = nums[start]; nums[start] = temp; ++start; --end; } } }

浙公网安备 33010602011771号