LeetCode Notes_#384_打乱数组
LeetCode Notes_#384_打乱数组
Contents
题目
解答
方法1: 暴力法
算法步骤:
- 将
array
复制到aux
数组当中 - 每次从
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),aux
是ArrayList
类型,每次进行remove()
操作的复杂度是O(n),再加上外边的循环,就是O(n2)
空间复杂度:O(n)
,复制了一份原数组到aux
和original
方法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个考点:
- 如何随机打乱,使得每种排列随机出现
- java当中的数组的
clone
方法的理解- 如果数组元素是基本类型,不存在浅拷贝,会直接创建新的一个数组,可以理解为深拷贝
- 如果数组元素是类对象,其实数组保存的是对象的引用,这时clone拷贝的是数组里的那些引用变量,新的引用变量和旧的引用变量指向的是同一个内存空间,修改了一个,另一个也会跟着改变,可以理解为是浅拷贝
本题当中涉及到clone
的数组元素类型都是基本类型int
,所以都可以看作是深拷贝,clone
会新开辟一片内存空间,保存新的数组,新旧数组不会互相影响。