夯实算法-随机数索引

165 阅读2分钟

题目:随机数索引

给你一个可能含有 重复元素 的整数数组 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 之一。每个索引的返回概率应该相等。

提示:

  • 1<=nums.length<=21041 <= nums.length <= 2 * 10^4
  • 231<=nums[i]<=2311-2^{31} <= nums[i] <= 2^{31} - 1
  • target 是 nums 中的一个整数
  • 最多调用 pick 函数 10410^4 次

解题思路

根据题意分析:

为了在数组中随机选择目标数字的下标,需要记录每个数字在数组中出现的下标,对于每个目标数字,首先得到该数字的下标列表,然后等概率地选择一个下标。可以使用哈希表记录每个数字在数组中出现的下标。

初始化时,遍历数组中的每个整数,并将下标添加到该整数对应的下标列表中。 随机选择下标时,首先得到目标数字的下标列表,然后在列表中等概率地选择一个下标并返回。

水塘抽样算法可以使用常数空间实现等概率选取样本。水塘抽样算法解决的问题是在 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()));
}

运行结果

Snipaste_2023-06-13_23-34-47.png

复杂度分析

  • 空间复杂度:O(1)
  • 时间复杂度:O(n)

掘金(JUEJIN) 一起分享知识, Keep Learning!