710. Random Pick with Blacklist 黑名单中的随机数(Hard)

本文介绍了一种优化算法,用于在给定的黑名单中从[0, n)范围内选择一个随机数,尽可能减少调用内置的random()函数。通过建立黑名单哈希表,并将黑名单中的数移到数组末尾,可以高效地实现随机选择,确保每次仅需调用一次random()。详细步骤包括创建哈希表以快速查询黑名单,以及在前半段白名单范围内取随机数并处理可能的黑名单情况。" 110216498,10296853,移动平均法详解:中心移动平均与差分时间序列预测,"['时间序列分析', '预测模型', '数据预测', '统计模型', '物流预测']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 描述

  • 给定一个包含 [0,n) 中不重复整数的黑名单 blacklist ,写一个函数从 [0, n) 中返回一个不在 blacklist 中的随机整数。
    对它进行优化使其尽量少的调用Math.random()

  • You are given an integer n and an array of unique integers blacklist. Design an algorithm to pick a random integer in the range [0, n - 1] that is not in blacklist. Any integer that is in the mentioned range and not in blacklist should be equally likely returned.
    Optimize your algorithm such that it minimizes the call to the built-in random function of your language.
    Implement the Solution class:

    • Solution(int n, int[] blacklist) Initializes the object with the integer n and the blacklisted integers blacklist.
    • int pick() Returns a random integer in the range [0, n - 1] and not in blacklist. All the possible integers should be equally likely returned.

2. 分析

  • 第一想法:每次生成随机数都判断是否在黑名单内,在则重新生成,但这样的设计不符合尽量少调用random()的要求。
  • 大致思路:那么如何做到尽量少的调用random()呢?如果生成的随机数一定不在黑名单中,则每次pick()只用调用一次random(),这就需要我们把[0, n)看成一个数组,把黑名单中的数移到末尾,由于知道黑名单中有多少个数,我们就可以知道应该生成零到几的随机数了。
  • 具体思路:将黑名单中的数移到末尾如何实现呢?其实数组[0, n)相当于最终被分成了定长的两块,前一部分为白名单,后一部分为黑名单。正常来说想法是遍历原数组,如果在黑名单中,则把数交换到末尾。但这样做有两个问题:一是如何快速判断当前数在不在黑名单中,二是需要确定交换的数不在黑名单中。
  • 问题一的本质是如何快速查询一个数在不在一个集合中,可以通过把黑名单中的数存进哈希表来来解决。第二个问题在创建哈希表后也得到解决。同时在建立了哈希表后,就不再需要遍历数组了,只需要在哈希表中进行查找,如果哈希表中的数在数组的黑名单区间,则不用移动。
  • 整体算法为:
    • 把0~N分成两段,前半段为白名单,后半段为黑名单:先建立一个存有所有黑名单数的map便于查询,再将在前半段范围内的黑名单数与在后半段的白名单数建立映射(作为键值对加入map),这样相当于前半段都是白名单数
      在这里插入图片描述
    • 取随机数:只用在前半段内取随机数,若取到了黑名单数,则去map中取对应的白名单数作为返回值即可

3. 代码

class Solution {
    Random random;
    Map<Integer, Integer> blackmap;
    int thre;
    public Solution(int n, int[] blacklist) {
        random = new Random();
        blackmap = new HashMap<>();
        // 将blacklist中的数存入hashmap,便于查询一个数是否在blacklist中
        for(int b: blacklist){
            blackmap.put(b, 66);
        }
        thre = n - blacklist.length;
        int last = n - 1;
        for(int b: blacklist){
            if(b >= thre) continue;
            while(blackmap.containsKey(last)){
                last--;
            }
            // 在hashmap中存入替换的值
            blackmap.put(b, last);
            // 存值后last要记得--,不然下一个黑名单中的数依然会用这个值替换
            last--;
        }
    }
    
    public int pick() {
        int rand = random.nextInt(thre);
        if(blackmap.containsKey(rand)){
            return blackmap.get(rand);
        } else {
            return rand;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值