• 【710. 黑名单中的随机数】


    来源:力扣(LeetCode)

    描述:

    给定一个整数 n 和一个 无重复 黑名单整数数组 blacklist 。设计一种算法,从 [0, n - 1] 范围内的任意整数中选取一个 未加入 黑名单 blacklist 的整数。任何在上述范围内且不在黑名单 blacklist 中的整数都应该有 同等的可能性 被返回。

    优化你的算法,使它最小化调用语言 内置 随机函数的次数。

    实现 Solution 类:

    • Solution(int n, int[] blacklist) 初始化整数 n 和被加入黑名单 blacklist 的整数
    • int pick() 返回一个范围为 [0, n - 1] 且不在黑名单 blacklist 中的随机整数

    示例 1:

    输入
    ["Solution", "pick", "pick", "pick", "pick", "pick", "pick", "pick"]
    [[7, [2, 3, 5]], [], [], [], [], [], [], []]
    输出
    [null, 0, 4, 1, 6, 1, 0, 4]
    
    解释
    Solution solution = new Solution(7, [2, 3, 5]);
    solution.pick(); // 返回0,任何[0,1,4,6]的整数都可以。注意,对于每一个pick的调用,
                     // 0、1、4和6的返回概率必须相等(即概率为1/4)。
    solution.pick(); // 返回 4
    solution.pick(); // 返回 1
    solution.pick(); // 返回 6
    solution.pick(); // 返回 1
    solution.pick(); // 返回 0
    solution.pick(); // 返回 4
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    提示:

    • 1 <= n <= 109
    • 0 <= blacklist.length <= min(105, n - 1)
    • 0 <= blacklist[i] < n
    • blacklist 中所有值都 不同
    • pick 最多被调用 2 * 104

    方法 :黑名单映射

    blacklist 的长度为 m

    考察一个特殊的例子:所有黑名单数全部在区间 [n − m, n) 范围内。此时我们可以直接在 [0, n − m) 范围内取随机整数。

    这给我们一个启示,对于在 [0, n − m) 范围内的黑名单数,我们可以将其映射到 [n − m, n) 范围内的非黑名单数(白名单数)上。每次 pick() 时,仍然可以在 [0, n − m) 范围内取随机整数(设其为 x),那么:

    如果 x 不在黑名单中,则直接返回 x
    如果 x 在黑名单中,则返回 x 映射到 [n − m, n) 范围内的白名单数。
    我们可以在初始化时,构建一个从 [0, n − m) 范围内的黑名单数到 [n − m, n) 的白名单数的映射:

    [n − m, n) 范围内的黑名单数存入一个哈希集合 black
    初始化白名单数 w = n − m
    对于每个 [0, n − m) 范围内的黑名单数 b,首先不断增加 w 直至其不在黑名单中,然后将 b 映射到 w 上,并将 w 增加一。

    代码:

    class Solution {
    private:
        unordered_map<int, int> mp;
        int bound;
    
    public:
        Solution(int n, vector<int> &blacklist) {
            int m = blacklist.size();
            bound = n - m;
            unordered_set<int> black;
            for (int b: blacklist) {
                if (b >= bound) {
                    black.emplace(b);
                }
            }
    
            int w = bound;
            for (int b: blacklist) {
                if (b < bound) {
                    while (black.count(w)) {
                        ++w;
                    }
                    mp[b] = w++;
                }
            }
        }
    
        int pick() {
            int x = rand() % bound;
            return mp.count(x) ? mp[x] : x;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    执行用时:108 ms, 在所有 C++ 提交中击败了89.64%的用户
    内存消耗:68.6 MB, 在所有 C++ 提交中击败了65.49%的用户
    复杂度分析
    时间复杂度:初始化为 O(m), pick() 为 O(1),其中 m 是数组 blacklist 的长度。在初始化结束时, [n − m, n) 内的每个数字要么是黑名单数,要么被一个黑名单数所映射,因此 white 会恰好增加 m 次,因此初始化的时间复杂度为 O(m)。
    空间复杂度: O(m)。哈希表需要 O(m) 的空间。
    author:LeetCode-Solution

  • 相关阅读:
    ArcGIS基础:点要素分割线要素和提取线要素的交点
    Flink与RabbitMQ Connector
    freeswitch1.10.7 安装&部署排坑
    RTOS: 堆和栈
    Neo4j源码研究系列 - 源代码准备
    网页翻译软件-网页自动采集翻译软件免费
    算法之滑动窗口
    React入门到精通学习路线
    8+非肿瘤+线粒体+实验生信思路解析
    使用QEMU+GDB调试操作系统代码
  • 原文地址:https://blog.csdn.net/Sugar_wolf/article/details/125467693