LeetCode 0710.黑名单中的随机数 - 预处理实现O取值
Posted Tisfy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 0710.黑名单中的随机数 - 预处理实现O取值相关的知识,希望对你有一定的参考价值。
【LetMeFly】710.黑名单中的随机数 - 预处理实现O(1)取值
力扣题目链接:https://leetcode.cn/problems/random-pick-with-blacklist/
给定一个整数 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 <= n <= 109
0 <= blacklist.length <- min(105, n - 1)
0 <= blacklist[i] < n
blacklist
中所有值都 不同-
pick
最多被调用2 * 104
次
方法一:预处理实现O(1)取值
这一题我们可以预处理一下:把 [ 0 , min ( n , 1 0 5 ) ) [0, \\min(n, 10^5)) [0,min(n,105))中未出现的数字记录下来。
具体方法为:
把blacklist
排序,使用一个指针初始值指向下标0
。
i i i从 [ 0 , m i n ( n , 1 0 5 ) ) [0, min(n, 10^5)) [0,min(n,105))遍历每一个数,如果指针已经达到了blacklist
的结尾,就记录下当前遍历到的 i i i,否则看 指 针 所 指 元 素 指针所指元素 指针所指元素和 i i i是否相同:若相同就说明 i i i在blacklist
中,指针后移并且不记录 i i i;否则记录下 i i i
上述算法得益于“blacklist
中的值互补相同”。如果blacklist
中存在相同的值,那么在“ 指 针 所 指 元 素 = i 指针所指元素=i 指针所指元素=i”时,指针不断后移,直到 当 前 指 针 所 指 元 素 ≠ i 当前指针所指元素\\neq i 当前指针所指元素=i
知道了 [ 0 , min ( n , 1 0 5 ) ) [0, \\min(n, 10^5)) [0,min(n,105))中所有未出现过的数字,我们就能用 O ( 1 ) O(1) O(1)的时间rand出一个值来。
具体方法为:
我们知道了 [ 0 , min ( n , 1 0 5 ) ) [0, \\min(n, 10^5)) [0,min(n,105))中未出现的数字,就能求得 [ 0 , n ) [0,n) [0,n)中所有未出现过的数字的个数: a l l N u m = [ 0 , min ( n , 1 0 5 ) ) 中 未 出 现 的 数 字 的 个 数 + [ 1 0 5 , n ) 的 所 有 数 字 的 个 数 allNum=[0,\\min(n, 10^5))中未出现的数字的个数+[10^5, n)的所有数字的个数 allNum=[0,min(n,105))中未出现的数字的个数+[105,n)的所有数字的个数
这样只需要一次rand( t h = r a n d ( ) % a l l N u m th = rand() \\% allNum th=rand()%allNum就能在[0, allNum)范围内rand),拿 t h th th和 [ 0 , n ) [0,n) [0,n)中所有未出现过的数字的个数做比较:小于则返回 [ 0 , n ) [0,n) [0,n)中第 t h + 1 th + 1 th+1个未出现的值;否则返回从 1 e 5 1e5 1e5开始的第 t h − s m a l l N u m + 1 th - smallNum + 1 th−smallNum+1个值
- 时间复杂度 O ( N log N + M ) O(N\\log N + M) O(NlogN+M),其中 N = m i n ( n , 1 0 5 ) N=min(n, 10^5) N=min(n,105), M M M为调用次数。时间复杂度主要来自预处理,之后每次调用都只需要 O ( 1 ) O(1) O(1)的时间复杂度。
- 空间复杂度 O ( S log S ) O(S\\log S) O(SlogS),其中 S = b l a c k l i s t . l e n g t h S=blacklist.length S=blacklist.length。空间复杂度主要来自预处理的排序( O ( S log S ) O(S\\log S) O(SlogS)),只需要 O ( S ) O(S) O(S)的额外空间存储未出现过的值。
AC代码
C++
class Solution
private:
vector<int> smallCan; // 小范围数据可选取
int n;
int smallTo;
int smallNum, allNum;
public:
Solution(int n, vector<int>& blacklist): n(n)
sort(blacklist.begin(), blacklist.end());
int loc = 0;
int size = blacklist.size();
smallTo = min(100000, n);
for (int i = 0; i < smallTo; i++)
if (loc == size)
smallCan.push_back(i);
else
if (i == blacklist[loc]) // blacklist 中所有值都 不同
loc++;
else
smallCan.push_back(i);
smallNum = smallCan.size();
allNum = smallNum + (n - smallTo);
int pick()
int th = rand() % allNum;
if (th < smallNum)
return smallCan[th];
else
return smallTo + (th - smallNum);
;
同步发文于CSDN,原创不易,转载请附上原文链接哦~
Tisfy:https://letmefly.blog.csdn.net/article/details/125466455
以上是关于LeetCode 0710.黑名单中的随机数 - 预处理实现O取值的主要内容,如果未能解决你的问题,请参考以下文章
[LeetCode] Random Pick with Blacklist 带黑名单的随机选取
710. Random Pick with Blacklist - LeetCode
数据结构与算法之深入解析“黑名单中的随机数”的求解思路与算法示例