题目链接:
力扣https://leetcode-cn.com/problems/random-pick-index/
【分析】方法一是通过一个HashMap嵌套的链表存下所有的下标,根据target可以以O(1)的复杂度取到这个下标链表,再随机选择一个。
class Solution {
Map<Integer, List<Integer>> map = new HashMap<>();
public Solution(int[] nums) {
int n = nums.length;
for(var i = 0; i < n; ++i){
if(map.containsKey(nums[i])){
map.get(nums[i]).add(i);
}else{
var list = new ArrayList<Integer>();
list.add(i);
map.put(nums[i], list);
}
}
}
public int pick(int target) {
var list = map.get(target);
var n = list.size();
var idx = (int)(n * Math.random());
return list.get(idx);
}
}
【方法二 水塘抽样】水塘抽样可以理解为,有大量的数据一次不同同时读入内存中,我们想从中随机等概率地抽出k个来, 要保证每次抽到的概率为k/n。
具体的算法流程为,先选出k个元素放入水塘中,对于第i项元素,随机生成一个[0, i)的数r,如果这个数<k,那么将水塘中第r个数替换成第i项元素。
证明如下:
class Solution {
public int[] nums;
public int n;
Random random = new Random();
public Solution(int[] nums) {
this.nums = nums;
n = nums.length;
}
public int pick(int target) {
var j = 0;
int ans = 0;
for(var i = 0; i < n; ++i){
if(nums[i] == target){
if(random.nextInt(++j) < 1) ans = i;
}
}
return ans;
}
}