这是 LeetCode 上2021-8-30的每日一题:「528. 按权重随机选择」
1. 题目描述
给定一个正整数数组w
,其中w[i]
代表下标i
的权重(下标从0
开始),请写一个函数pickIndex
,它可以随机地获取下标i
,选取下标i
的概率与w[i]
成正比。
例如,对于w = [1, 3]
,挑选下标0
的概率为1 / (1 + 3) = 0.25
(即,25%),而选取下标1
的概率为3 / (1 + 3) = 0.75
即,75%)。
也就是说,选取下标i
的概率为w[i] / sum(w)
。
示例 1:
输入:
["Solution","pickIndex"]
[[[1]],[]]
输出:
[null,0]
解释:
Solution solution = new Solution([1]);
solution.pickIndex(); // 返回 0,因为数组中只有一个元素,所以唯一的选择是返回下标 0。
示例 2:
输入:
["Solution","pickIndex","pickIndex","pickIndex","pickIndex","pickIndex"]
[[[1,3]],[],[],[],[],[]]
输出:
[null,1,1,1,1,0]
解释:
Solution solution = new Solution([1, 3]);
solution.pickIndex(); // 返回 1,返回下标 1,返回该下标概率为 3/4 。
solution.pickIndex(); // 返回 1
solution.pickIndex(); // 返回 1
solution.pickIndex(); // 返回 1
solution.pickIndex(); // 返回 0,返回下标 0,返回该下标概率为 1/4 。
由于这是一个随机问题,允许多个答案,因此下列输出都可以被认为是正确的:
[null,1,1,1,1,0]
[null,1,1,1,1,1]
[null,1,1,1,0,0]
[null,1,1,1,0,1]
[null,1,0,1,0,0]
......
诸若此类。
2. 解答
例如输入数组为[1,2,3,4]
,则划分:[1,1], [2,3], [4,5,6], [7,8,9,10]
,求出前缀和数组[1,3,6,10]
。
生成[1,10]
之间的一个随机整数,用二分查找在前缀和数组中寻找满足区间条件的最小下标i
,并返回。
class Solution {
constructor(w) {
this.len = w.length;
// 定义前缀和数组
this.pre = new Array(this.len).fill(0);
this.pre[0] = w[0];
// 从i=1开始,求每个位置的前缀和
for (let i = 1; i < this.len; i++) {
this.pre[i] = this.pre[i - 1] + w[i];
}
console.log(this.pre);
// 总和
this.total = this.pre[this.len - 1];
}
pickIndex() {
// Math.random()返回 0 ~ 1 之间的一个随机数
// x 是 1 ~ total 之间的一个随机整数
const x = Math.floor(Math.random() * this.total) + 1;
// 二分查找
let [low, high] = [0, this.len - 1];
while (low <= high) {
const mid = (low + high) >> 1;
if (this.pre[mid] < x) low = mid + 1;
else high = mid - 1;
}
return low;
}
}
😄最近新创建了个开源仓库,总结LeetCode的每日一题,目前已有C++、JavaScript语言版本,欢迎大家提供其他语言版本!
🖥️仓库地址:「每日一题系列」