[398]. 随机数索引

Posted Debroon

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[398]. 随机数索引相关的知识,希望对你有一定的参考价值。

[398]. 随机数索引

 


题目

传送门:398. 随机数索引

给你一个可能含有 重复元素 的整数数组 nums ,请你随机输出给定的目标数字 target 的索引。

// 实现 Solution 类:
- Solution(int[] 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 之一。每个索引的返回概率应该相等。

 


算法设计:哈希思路

哈希算法一般是用一个 hash 数组来记录一个 key 对应的有无或者统计对应的个数,用 key 作为 hash 数组的下标,而下标对应的值用来表示该 key 的存在与否以及统计对应的个数。

比如,传入参数是 [1, 2, 3, 3, 3]。

  • 值为 1 的索引有哪些,放入一个数组中
  • 值为 2 的索引有哪些,放入一个数组中
  • 值为 3 的索引有哪些,放入一个数组中

再从某个匹配数组中,随机选取一个,这样就实现了等概率。

class Solution 
    unordered_map<int, vector<int>> pos;
public:
    Solution(vector<int> &nums) 
        int n = nums.size();    // 缓存,减少不必要的重复计算
        for (int i = 0; i < n; ++i) 
            pos[nums[i]].push_back(i);
            // 值为 nums[i] 的索引有哪些,放入一个数组
    

    int pick(int target) 
        return pos[target][rand() % pos[target].size()];
        // 再从某个匹配数组中,随机选取一个
    
;

时间复杂度: θ ( n ) \\theta(n) θ(n)

空间复杂度: θ ( n ) \\theta(n) θ(n)
 


模偏差:rand( ) 的不公平问题

rand() % 3

rand() 会生成一个 0 - 32767(INT_MAX - 1) 的数。

  • rand() % 3 = 0,等于 0 的情况,有 10923 个
  • rand() % 3 = 1,等于 1 的情况,有 10923 个
  • rand() % 3 = 2,等于 2 的情况,有 10922

因为整型最大值的限制,其实会产生一个模偏差,并没有实现真正的概率平等(公平)。

但是 OJ 平台因为数据量较少,所以可以过。

但,尽管如此,也是没有真正公平的。
 


算法设计:蓄水池抽样

以上是关于[398]. 随机数索引的主要内容,如果未能解决你的问题,请参考以下文章

398. 随机数索引

398 Random Pick Index 随机数索引

398. 随机数索引Normal水塘抽样算法

「 每日一练,快乐水题 」398. 随机数索引

「 每日一练,快乐水题 」398. 随机数索引

LeetCode 398 随机数索引[水塘抽样] HERODING的LeetCode之路