持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第28天,点击查看活动详情
一.题目:
710. 黑名单中的随机数 给定一个整数
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 <= 1090 <= blacklist.length <= min(105, n - 1)0 <= blacklist[i] < nblacklist中所有值都 不同-
pick最多被调用2 * 10^4次
二、思路分析:
还是仔细的看题目,题目要求我们给定一个n,给定一个黑名单的数组,每次寻找一个值,这个值在0--n-1之间,如果选中了黑名单的数需要重新选择,但是题目又要求优化算法,使得最小化内置随机选择的函数。所以基本的步骤如下:
- 如果想要高效地等概率的选择元素,最佳的做法就是利用
对象进行选择。 - 我们想象成是一个数组存放着这些元素,我们从末尾出发,如果元素在黑名单内则跳过,如果黑名单元素在
0--n - blacklist.length之间我们就可以使该地方的元素访问末尾不在黑名单的元素。 - 如此我们的随机选择元素函数就是极小程度的选择了不在黑名单的元素。
三、代码:
class Solution {
numMap = {};
n: number;
constructor(n: number, blacklist: number[]) {
this.n = n - blacklist.length;
for(let num of blacklist){
this.numMap[num] = -2;
}
let cur = n - 1;
for(let num of blacklist){
if(num >= this.n) continue;
while(this.numMap[cur]) cur--;
this.numMap[num] = cur;
cur--;
}
}
pick(): number {
let randomIndex = Math.floor(Math.random() * this.n);
return this.numMap[randomIndex] || randomIndex;
}
}
/**
* Your Solution object will be instantiated and called as such:
* var obj = new Solution(n, blacklist)
* var param_1 = obj.pick()
*/
四、总结:
这道题的思路比较难想到,一般这种
O(1)插入、删除元素的操作都可以在数组上进行操作,记住这一点后我们就可以较容易地解决此类问题。