旋转数组 [ LeetCode ]

原题地址:https://leetcode-cn.com/problems/rotate-array/description/

给定一个数组,将数组中的元素向右移动 个位置,其中 是非负数。

示例 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

  坦白说,这个方法我没想明白什么原理,但的确很神奇,如果有朋友明白里的原理,还请不吝赐教啊。。。。。

猜你喜欢

转载自www.cnblogs.com/zfLee/p/9328633.html