题目来源:https://leetcode-cn.com/problems/shuffle-an-array/
大致题意:
设计一个类,可以完成以下操作:
- 初始化,使用给定的数组 nums 初始化对象
- 重置,将数组重置到初始状态、
- 打乱,随机将数组打乱
思路
需求分析:
- 初始化一个数组,就需要类本身有属性为数组
- 重置数组到初始状态,需要提前保存初始状态
- 随机打乱数组,按照力扣的一贯风格,需要设计一个概率平均的随机算法,即各个位置被打乱的概率应该相同
具体实现:
- 设置一个属性为操作数组,用于初始化和打乱
- 再设置一个属性为重置数组,用于存初始化时侯的数组,在重置的时候将该数组内容赋值给操作数组
- 设计打乱算法
打乱算法
- 将原数组所有元素放入一个容器中
- 随即从容器中取出一个元素,放到新数组(初始为空)末尾
- 重复第二步,直至容器元素为空
这样保证了每个元素被选中的几率是相同的:
比如,对于第 i 个元素,它被选中为新数组第一个的概率是 i / n,如果未被选中第一个,那么被选为第二个概率为 [(n - 1) / n] * [1 / (n - 1)],即 1 / n,递推下去,会发现它被选为任意位置的概率都相等
代码:
public class Solution {
int[] nums;
int[] origin;
public Solution(int[] nums) {
this.nums = nums;
// 内存拷贝,而不是直接指向
// 否则会导致两个属性指向同一块内存
this.origin = Arrays.copyOf(nums, nums.length);
}
public int[] reset() {
// 内存拷贝
nums = Arrays.copyOf(origin, origin.length);
return nums;
}
public int[] shuffle() {
// 新数组
int[] shuffled = new int[nums.length];
// 将原数组的元素放入集合中
List<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < nums.length; ++i) {
list.add(nums[i]);
}
// 随机数对象
Random random = new Random();
for (int i = 0; i < nums.length; ++i) {
// 在当前的容器中随机选出一个数,放到新数组末尾,并从集合中移除
int j = random.nextInt(list.size());
shuffled[i] = list.remove(j);
}
// 将新数组拷贝给操作数组
nums = Arrays.copyOf(shuffled, shuffled.length);
return nums;
}
}