先从Leetcode一个问题 打乱数组 开始。题目的要求是构造一个类,用来,用来保存传入的数组,类提供两个方法,一个用来还原数组为传入时的状态,另一个用来打乱数组。
类模板如下
class Solution {
public Solution(int[] nums) {
}
/** Resets the array to its original configuration and return it. */
public int[] reset() {
}
/** Returns a random shuffling of the array. */
public int[] shuffle() {
}
}
该问题有两个解法
- 解法一:
使用一个List存储数组中的元素,从i = 0开始填充数组中的元素,每次从List中随机选择一个元素放入数组中,放入之后,移除List中的元素,这样就不会重复放入,由于要多次进行插入和删除操作,我们使用LinkedList即可
class Solution {
int[] nums;
int[] origin;
Random random = new Random();
public Solution(int[] nums) {
this.nums = nums;
this.origin = nums.clone();
}
/** Resets the array to its original configuration and return it. */
public int[] reset() {
this.nums = origin.clone();
return nums;
}
/** Returns a random shuffling of the array. */
public int[] shuffle() {
List<Integer> aux = getArray();
for (int i = 0; i < nums.length; ++i) {
int index = random.nextInt(aux.size());
nums[i] = aux.get(index);
aux.remove(index);
}
return nums;
}
private List<Integer> getArray() {
List<Integer> aux = new LinkedList<>();
for (int i = 0; i < nums.length; ++i) {
aux.add(nums[i]);
}
return aux;
}
}
- 解法二:
Fisher-Yates 洗牌算法:从数组 i = 0的位置开始,将当前位置的元素与数组中的元素随机交换,这样同样能保证数组元素是随机分布的,而且这样还可以节省一个List的空间。ps:许多语言中提供的shuffle方法也是基于这个算法。
class Solution {
int[] nums;
int[] origin;
Random random = new Random();
public Solution(int[] nums) {
this.nums = nums;
this.origin = nums.clone();
}
/** Resets the array to its original configuration and return it. */
public int[] reset() {
this.nums = origin.clone();
return nums;
}
/** Returns a random shuffling of the array. */
public int[] shuffle() {
for (int i = 0; i < nums.length; ++i) {
int random = randomRange(nums.length, i);
swap(i, random, nums);
}
return nums;
}
private int randomRange(int max, int min) {
return min + random.nextInt(max - min);
}
private void swap (int i, int j, int[] arr) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}