Given a blacklist B
containing unique integers from [0, N)
, write a function to return a uniform random integer from [0, N)
which is NOT in B
.html
Optimize it such that it minimizes the call to system’s Math.random()
.數組
Note:app
1 <= N <= 1000000000
0 <= B.length < min(100000, N)
[0, N)
does NOT include N. See interval notation.Example 1:dom
Input:
["Solution","pick","pick","pick"]
[[1,[]],[],[],[]] Output: [null,0,0,0]
Example 2:函數
Input:
["Solution","pick","pick","pick"]
[[2,[]],[],[],[]] Output: [null,1,1,1]
Example 3:post
Input:
["Solution","pick","pick","pick"]
[[3,[1]],[],[],[]] Output: [null,0,0,2]
Example 4:url
Input:
["Solution","pick","pick","pick"]
[[4,[2]],[],[],[]] Output: [null,1,3,1]
Explanation of Input Syntax:spa
The input is two lists: the subroutines called and their arguments. Solution
's constructor has two arguments, N
and the blacklist B
. pick
has no arguments. Arguments are always wrapped with a list, even if there aren't any.code
這道題讓咱們生成一個N之內的隨機數,可是還給了一個黑名單,意思是黑名單裏面的數字不能被選到。因而博主最早想到的方法就是用拒絕採樣Rejection Sampling來作,由於以前作過使用該方法的兩道題 Implement Rand10() Using Rand7() 和 Generate Random Point in a Circle,因此能夠立馬想到。思路其實很簡單,就是隨機一個數,若是是黑名單裏的,那麼就從新隨機。爲了提升在黑名單中查找數字的速度,咱們將全部黑名單的數字放到一個HashSet中,這樣咱們就擁有了常數級查找的速度,看似一切水到渠成,燃鵝被OJ強行打臉,TLE!那麼換一種思路吧,既然你有黑名單,那麼林北就有白名單,把全部沒被block的數字都放到一個新數組中,而後隨機生成數組座標不就完了。燃鵝x2,又被OJ放倒了,MLE!不許用這麼多內存。豈可修,真的沒別的辦法了嘛?!還好方法解答貼中給了一種使用HashMap的方法來作,博主仔細研讀了一番,發現確實秒啊!既然數字總共有N個,那麼減去黑名單中數字的個數,就是最多能隨機出來的個數。好比N=5,黑名單中有兩個數{2, 4},那麼咱們最多隻能隨機出三個,可是咱們若是直接rand()%3,會獲得0,1,2,咱們發現有兩個問題,一是黑名單中的2能夠隨機到,二是數字3無法隨機到。那麼咱們想,能不能隨機到0或1則返回其自己,而當隨機到2到時候,咱們返回的是3,咱們須要創建這樣的映射,這就是使用HashMap的動機啦。咱們首先將超過N - blacklist.size()的數字放入一個HashSet,這裏就把{3, 4}放進去了,而後咱們遍歷blacklist中的數字,若是在HashSet中的話,就將其刪除,這樣HashSet中就只有{3}了,這個須要創建映射的數字,而用什麼數字創建,固然是用黑名單中的數字了,遍歷黑名單中的數字,若是小於N - blacklist.size()的話,說明是有可能隨機到的,咱們和HashSet中的第一個數字創建映射,而後咱們能夠用個iterator,指向HashSet中的下一個數組,而後繼續創建映射。從而實如今pick函數中的移魂換影大法了,先隨機個數字,若是有映射,則返回映射值,不然返回原數字,參見代碼以下:orm
class Solution { public: Solution(int N, vector<int> blacklist) { unordered_set<int> st; len = N - blacklist.size(); for (int i = len; i < N; ++i) st.insert(i); for (int num : blacklist) st.erase(num); auto it = st.begin(); for (int num : blacklist) { if (num < len) m[num] = *it++; } } int pick() { int k = rand() % len; return m.count(k) ? m[k] : k; } private: unordered_map<int, int> m; int len; };
相似題目:
參考資料:
https://leetcode.com/problems/random-pick-with-blacklist/