LeetCode Notes_#384_打乱数组

LeetCode Notes_#384_打乱数组

Contents

题目


解答

方法1: 暴力法

算法步骤:

  1. array复制到aux数组当中
  2. 每次从aux数组当中随机抽取一个数字,并删除这个数字,直到aux数组为空

为什么这样做可以保证随机性?

class Solution {
    private int[] array;
    private int[] original;
    private Random rand = new Random();

    private List<Integer> getArrayCopy(){
        List<Integer> asList = new ArrayList<Integer>();
        for(int i = 0;i < array.length;i++){
            asList.add(array[i]);
        }
        return asList;
    }

    public Solution(int[] nums) {
        array = nums;
        original = nums.clone();
    }
    
    /** Resets the array to its original configuration and return it. */
    public int[] reset() {
        array = original;
        original = original.clone();
        return array;
    }
    
    /** Returns a random shuffling of the array. */
    public int[] shuffle() {
        List<Integer> aux = getArrayCopy();
        for(int i = 0;i < array.length;i++){
            int removeIdx = rand.nextInt(aux.size());
            array[i] = aux.get(removeIdx);
            aux.remove(removeIdx);
        }
        return array;
    }
}

复杂度分析

时间复杂度:O(n2),auxArrayList类型,每次进行remove()操作的复杂度是O(n),再加上外边的循环,就是O(n2)
空间复杂度:O(n),复制了一份原数组到auxoriginal

方法2: Fisher-Yates洗牌算法

算法步骤:
遍历数组array当中的每个数字,每次随机选出这个数字之后的一个其他数字与其交换。
这样做的原理和方法1是类似的,不同的是,使用了交换的方式,避免了remove带来的O(n)的复杂度。

class Solution {
    //array是每次调用shuffle()就会发生变化的数组
    int[] array;
    //original是在构造器中将原始数组存了下来,在reset()时将array恢复到和original相同
    int[] original;
    Random random = new Random();

    public Solution(int[] nums) {
        array = nums;
        //这里是深拷贝一个nums数组,而不能简单的使用 original = nums 
        //原因: array和original如果指向了同一个nums内存空间,之后对nums进行修改的时候,也会影响到original
        original = nums.clone();
    }

    //生成范围在[i,array.length - 1]范围内的一个随机整数作为下标
    private int randRange(int min, int max){
        return min + random.nextInt(max - min);
    }

    private void swap(int i, int j){
        int tmp = array[i];
        array[i] = array[j];
        array[j] = tmp;
    }
    
    /** Resets the array to its original configuration and return it. */
    public int[] reset() {
        //将原始数组拷贝一份,然后让array指向新的拷贝数组
        array = original.clone();
        return array;
    }
    
    /** Returns a random shuffling of the array. */
    public int[] shuffle() {
        for(int i = 0;i <= array.length - 1;i++){
            //注意:nextInt返回值是<array.length,即最大为array.length - 1
            int randIdx = randRange(i, array.length);
            swap(i, randIdx);
        }
        return array;
    }
}

复杂度分析

时间复杂度:O(n)
空间复杂度:O(n)

关于java中的clone()方法

本题有2个考点:

  1. 如何随机打乱,使得每种排列随机出现
  2. java当中的数组的clone方法的理解
    • 如果数组元素是基本类型,不存在浅拷贝,会直接创建新的一个数组,可以理解为深拷贝
    • 如果数组元素是类对象,其实数组保存的是对象的引用,这时clone拷贝的是数组里的那些引用变量,新的引用变量和旧的引用变量指向的是同一个内存空间,修改了一个,另一个也会跟着改变,可以理解为是浅拷贝

本题当中涉及到clone的数组元素类型都是基本类型int,所以都可以看作是深拷贝,clone会新开辟一片内存空间,保存新的数组,新旧数组不会互相影响。

posted @ 2020-10-31 15:31  Howfar's  阅读(92)  评论(0编辑  收藏  举报