[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     // C语言产生随机数函数
random() % 3   // CPP 产生随机数函数

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

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

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

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

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

我们使用 random() 代替 rand(),虽然运算速度下降了一点点,不过是公平的。

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][random() % pos[target].size()];
        // 再从某个匹配数组中,等概率随机选取一个
    }
};

算法设计:蓄水池抽样

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值