LeetCode 189. Rotate Array (Java)

题目:

Given an array, rotate the array to the right by k steps, where k is non-negative.

Example 1:
Input: [1,2,3,4,5,6,7] and k = 3
Output: [5,6,7,1,2,3,4]
Explanation:
rotate 1 steps to the right: [7,1,2,3,4,5,6]
rotate 2 steps to the right: [6,7,1,2,3,4,5]
rotate 3 steps to the right: [5,6,7,1,2,3,4]

Example 2:
Input: [-1,-100,3,99] and k = 2
Output: [3,99,-1,-100]
Explanation:
rotate 1 steps to the right: [99,-1,-100,3]
rotate 2 steps to the right: [3,99,-1,-100]

解析:

假设n=7,k=3
原始数组: [1,2,3,4,5,6,7]
反转所有数字后: [7,6,5,4,3,2,1]
反转前k个数字后: [5,6,7,4,3,2,1]
反转后n-k个数字后: [5,6,7,1,2,3,4]

//当我们旋转数组k次,k\%n个尾部元素会被移动到头部,剩下的元素会被向后移动
//先将所有元素反转,然后反转前k个元素,再反转后面n-k个元素,就能得到想要的结果

class Solution {
    public void rotate(int[] nums, int 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[start];
            nums[start]=nums[end];
            nums[end]=temp;
            start++;
            end--;
        }
    }
}

官方给出了多种方法,如方法1:暴力解法

//最简单的方法是旋转k次,每次将数组旋转1个元素
//时间复杂度:O(n*k),每个元素都被移动1步O(n),k次O(k)
//空间复杂度:O(1),没有额外空间被使用
public class Solution {
    public void rotate(int[] nums, int k) {
        int temp, previous;
        for (int i = 0; i < k; i++) {
            previous = nums[nums.length - 1];
            for (int j = 0; j < nums.length; j++) {
                temp = nums[j];
                nums[j] = previous;
                previous = temp;
            }
        }
    }
}

方法2:使用额外的数组

//用一个额外的数组来将每个元素放到正确的位置上,也就是原本数组里下标为i的,把它放到 (i+k)\%数组长度的位置,然后把新的数组拷贝到原数组中
//时间复杂度:O(n),将数字放到新的数组中需要一遍遍历,另一边来把新数组的元素拷贝回原数组
//空间复杂度:O(n),另一个数组需要原数组长度的空间

public class Solution {
    public void rotate(int[] nums, int k) {
        int[] a = new int[nums.length];
        for (int i = 0; i < nums.length; i++) {
            a[(i + k) % nums.length] = nums[i];
        }
        for (int i = 0; i < nums.length; i++) {
            nums[i] = a[i];
        }
    }
}

方法3:使用环状替换
如果我们直接把每一个数字放到它最后的位置,但这样的后果是遗失原来的元素。因此,我们需要把被替换的数字保存在变量temp里面。然后,我们将被替换数字(temp)放到它正确的位置,并继续这个过程n次,n是数组的长度。这是因为我们需要将数组里所有的元素都移动。但是,这种方法可能会有个问题,如果n%k = = 0,其中 k=k%n(因为如果k大于n,移动k次实际上相当于移动k%n次)。这种情况下,我们会发现在没有遍历所有数字的情况下回到出发数字。此时,我们应该从下一个数字开始再重复相同的过程。
现在,我们看看上面方法的证明。假设,数组里我们有n个元素并且k是要求移动的次数。更进一步,假设n%k=0。第一轮中,所有移动数字的下标i满足i%k== 0。这是因为我们每跳k步,我们只会到达相距为k个位置下标的数。每一轮,我们都会移动n/k个元素。下一轮中,我们会移动满足i%k == 1的位置的数。这样的轮次会一直持续到我们再次遇到i%k==0的地方为止,此时 i=k。此时在正确位置上的数字共有k*(n/k)=n 个。因此所有数字都在正确位置上。
如:nums: [1, 2, 3, 4, 5, 6]
k: 2
LeetCode 189. Rotate Array (Java)_第1张图片

//时间复杂度:O(n),只遍历了每个元素一次
//空间复杂度:O(1),使用了常数个额外空间
public class Solution {
    public void rotate(int[] nums, int k) {
        k = k % nums.length;
        int count = 0;
        for (int start = 0; count < nums.length; start++) {
            int current = start;
            int prev = nums[start];
            do {
                int next = (current + k) % nums.length;
                int temp = nums[next];
                nums[next] = prev;
                prev = temp;
                current = next;
                count++;
            } while (start != current);
        }
    }
}

你可能感兴趣的:(LeetCode)