旋转数组 [ LeetCode ]
原题地址:https://leetcode-cn.com/problems/rotate-array/description/
给定一个数组,将数组中的元素向右移动 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) 的原地算法。
以上是原题
这道算法题目本身并不难,但要求使用三种不同方法解决问题,这就考验小伙伴们脑洞能开多大了。。。
解法一:
首先用最普通的解法,根据题目描述,我们不难得出,将一个元素右移k个位置,原处于index位置的元素会出现在index + k 的位置上,如果index + k 超出数组长度,那么从0开始接着移动,新的下标等价于 (index + k) % length.
直接上代码:
//旋转数组 public void rotate(int[] nums, int k) { int[] temp = Arrays.copyOf(nums, nums.length); for (int index = 0; index < temp.length; index++) { nums[(index + k) % temp.length] = temp[index]; } }
这种解法简单粗暴,效率也不错,但其实题目中要求尽量使用空间复杂度为O(1)的原地算法,上述解法使用了一个与输入数组同样大小的空间。
解法二:
移动第一个元素到k位置,将原本处在k位置的元素向右移动k位置,如果遇到了超出数组长度且指针指向数组第0个位置时,指针后移一位。
1 public void rotate(int[] nums, int k) { 2 if (nums.length == 0 || (k %= nums.length) == 0) { 3 return; 4 } 5 int length = nums.length; 6 int start = 0, index = 0, cnt = 0, temp = 0; 7 int cur = nums[index]; 8 while (cnt++ < length) { 9 index = (index + k) % length; 10 temp = nums[index]; 11 nums[index] = cur; 12 if (index == start) { //是否循环到已处理过的元素 13 ++start; 14 cur = nums[++index]; 15 } else { 16 cur = temp; 17 } 18 } 19 }
执行该方法时,数组状态变化如下:
测试数据: [2, 7, 11, 15, 12, 16] 向右移动2位
2 7 2 15 12 16 2 7 2 15 11 16 12 7 2 15 11 16 12 7 2 7 11 16 12 7 2 7 11 15 12 16 2 7 11 15
解法三:
这种方法比较特别,将原有数组用 length - k 分割为两段,分别对这两段中的数字进行首尾换位,然后再对整个数组进行首尾换位。
1 public void rotate(int[] nums, int k) { 2 k = k % nums.length; 3 reverse(nums, 0, nums.length - 1 - k); 4 reverse(nums, nums.length - k, nums.length - 1); 5 reverse(nums, 0, nums.length - 1); 6 } 7 8 private void reverse(int[] nums, int start, int end) { 9 while (start < end) { 10 int tmp = nums[start]; 11 nums[start++] = nums[end]; 12 nums[end--] = tmp; 13 } 14 }
同样给出数据状态:
测试数据: [2, 7, 11, 15, 12, 16] 向右移动2位
15 11 7 2 12 16 //第一次reverse 15 11 7 2 16 12 //第二次reverse 12 16 2 7 11 15 //第三次reverse
坦白说,这个方法我没想明白什么原理,但的确很神奇,如果有朋友明白里的原理,还请不吝赐教啊。。。。。