题目:随机数索引
给你一个可能含有 重复元素 的整数数组 nums ,请你随机输出给定的目标数字 target 的索引。你可以假设给定的数字一定存在于数组中。
实现 Solution 类:
Solution(int[] nums)用数组nums初始化对象。int pick(int target)从nums中选出一个满足nums[i] == target的随机索引i。如果存在多个有效的索引,则每个索引的返回概率应当相等。 示例:
输入
["Solution", "pick", "pick", "pick"]
[[[1, 2, 3, 3, 3]], [3], [1], [3]]
输出
[null, 4, 0, 2]
解释
Solution solution = new Solution([1, 2, 3, 3, 3]);
solution.pick(3); // 随机返回索引 2, 3 或者 4 之一。每个索引的返回概率应该相等。
solution.pick(1); // 返回 0 。因为只有 nums[0] 等于 1 。
solution.pick(3); // 随机返回索引 2, 3 或者 4 之一。每个索引的返回概率应该相等。
提示:
target是nums中的一个整数- 最多调用
pick函数 次
解题思路
根据题意分析:
为了在数组中随机选择目标数字的下标,需要记录每个数字在数组中出现的下标,对于每个目标数字,首先得到该数字的下标列表,然后等概率地选择一个下标。可以使用哈希表记录每个数字在数组中出现的下标。
初始化时,遍历数组中的每个整数,并将下标添加到该整数对应的下标列表中。 随机选择下标时,首先得到目标数字的下标列表,然后在列表中等概率地选择一个下标并返回。
水塘抽样算法可以使用常数空间实现等概率选取样本。水塘抽样算法解决的问题是在 mmm 个样本中等概率地随机抽取 k 个数据,这道题中 m 为 target 在数组中出现的次数,k=1。
使用水塘抽样随机选择下标的做法是,用 picked 表示随机选择的下标,用 count 表示遍历到的等于目标值的数字个数,初始时 picked=−1,count=0,遍历数组的过程中,当遍历到下标 iii 时,如果 nums[i]=target,则执行如下操作。
将 count 加 1。
在范围 [0,count−1] 中等概率地选择一个整数,如果选择的整数是 0 则用当前下标 i 更新随机选择的下标 picked。
遍历结束时,picked 即为随机选择的下标,返回 picked。
代码实现
Random random = new Random();
HashMap < Integer, List < Integer >> map = new HashMap < > ();
public Solution(int[] nums) {
List < Integer > index;
for (int i = 0; i < nums.length; i++) {
if (!map.containsKey(nums[i])) {
index = new ArrayList < > ();
map.put(nums[i], index);
} else {
index = map.get(nums[i]);
}
index.add(i);
}
}
public int pick(int target) {
List < Integer > index = map.get(target);
return index.get(random.nextInt(index.size()));
}
运行结果
复杂度分析
- 空间复杂度:O(1)
- 时间复杂度:O(n)
在掘金(JUEJIN) 一起分享知识, Keep Learning!