题目描述
给定一个可能含有重复元素的整数数组,要求随机输出给定的数字的索引。 您可以假设给定的数字一定存在于数组中。
注意:
数组大小可能非常大。 使用太多额外空间的解决方案将不会通过测试。
示例:
int[] nums = new int[] {1,2,3,3,3};
Solution solution = new Solution(nums);
// pick(3) 应该返回索引 2,3 或者 4。每个索引的返回概率应该相等。
solution.pick(3);
// pick(1) 应该返回 0。因为只有nums[0]等于1。
solution.pick(1);
嗯,似曾相识的随机数索引,我首先想到的是哈希表,但随机数的用法又忘记了,于是便去查看了官方的做法。本来我想着能不能直接通过值来索引键,后来发现我在想peach,只有键索引值,Key->Value,根本不可能反过来的。所以官方给每一个键对应的值都是整数列表List<Integer>,无论一个数字出现一次还是多次,都放到一个列表里。然后pick时通过随机数随机返回一个范围内的索引。
class Solution {
//声明
Random random;
Map<Integer,List<Integer>> map;
public Solution(int[] nums) {
//构造方法里初始化
random = new Random();
map = new HashMap();
for(int i=0;i<nums.length;i++){
//,遍历每一个数,如果没有初始化一个整数列表,就初始化
map.putIfAbsent(nums[i],new ArrayList<Integer>());
//往对应列表中加入索引
map.get(nums[i]).add(i);
}
}
public int pick(int target) {
//获取键target对应的索引列表
List<Integer> indices = map.get(target);
//随机返回一个索引
return indices.get(random.nextInt(indices.size()));
}
}
/**
* Your Solution object will be instantiated and called as such:
* Solution obj = new Solution(nums);
* int param_1 = obj.pick(target);
*/
当然官方还有一个空间复杂度为O(1)的水塘抽样法。这里引鉴一下:
class Solution {
int[] nums;
Random random;
public Solution(int[] nums) {
this.nums = nums;
random = new Random();
}
public int pick(int target) {
int ans = 0;
for (int i = 0, cnt = 0; i < nums.length; ++i) {
if (nums[i] == target) {
++cnt; // 第 cnt 次遇到 target
if (random.nextInt(cnt) == 0) {
ans = i;
}
}
}
return ans;
}
}
水塘抽样的基本概念我懂了,即当
n=1时,1的概率是1,
n=2时,2的概率是0.5,
n=3时,3的概率是0.33,
n=4时,4的概率是0.25……
但每增加一个数,前面的数就不可能再被选中了,
这种情况下为什么概率是一样的,还需要我以后慢慢研究。