Given an array w
of positive integers, where w[i]
describes the weight of index i
, write a function pickIndex
which randomly picks an index in proportion to its weight.html
Note:数组
1 <= w.length <= 10000
1 <= w[i] <= 10^5
pickIndex
will be called at most 10000
times.Example 1:app
Input:
["Solution","pickIndex"]
[[[1]],[]] Output: [null,0]
Example 2:dom
Input:
["Solution","pickIndex","pickIndex","pickIndex","pickIndex","pickIndex"]
[[[1,3]],[],[],[],[],[]] Output: [null,0,1,1,1,0]
Explanation of Input Syntax:函数
The input is two lists: the subroutines called and their arguments. Solution
's constructor has one argument, the array w
. pickIndex
has no arguments. Arguments are always wrapped with a list, even if there aren't any.spa
这道题给了一个权重数组,让咱们根据权重来随机取点,如今的点就不是随机等几率的选取了,而是要根据权重的不一样来区别选取。好比题目中例子2,权重为 [1, 3],表示有两个点,权重分别为1和3,那么就是说一个点的出现几率是四分之一,另外一个出现的几率是四分之三。因为咱们的rand()函数是等几率的随机,那么咱们如何才能有权重的随机呢,咱们可使用一个trick,因为权重是1和3,相加为4,那么咱们如今假设有4个点,而后随机等几率取一个点,随机到第一个点后就表示原来的第一个点,随机到后三个点就表示原来的第二个点,这样就能够保证有权重的随机啦。那么咱们就能够创建权重数组的累加和数组,好比若权重数组为 [1, 3, 2] 的话,那么累加和数组为 [1, 4, 6],整个的权重和为6,咱们 rand() % 6,能够随机出范围 [0, 5] 内的数,随机到 0 则为第一个点,随机到 1,2,3 则为第二个点,随机到 4,5 则为第三个点,因此咱们随机出一个数字x后,而后再累加和数组中查找第一个大于随机数x的数字,使用二分查找法能够找到第一个大于随机数x的数字的坐标,即为所求,参见代码以下:code
解法一:htm
class Solution { public: Solution(vector<int> w) { sum = w; for (int i = 1; i < w.size(); ++i) { sum[i] = sum[i - 1] + w[i]; } } int pickIndex() { int x = rand() % sum.back(), left = 0, right = sum.size() - 1; while (left < right) { int mid = left + (right - left) / 2; if (sum[mid] <= x) left = mid + 1; else right = mid; } return right; } private: vector<int> sum; };
咱们也能够把二分查找法换为STL内置的upper_bound函数,根据上面的分析,咱们随机出一个数字x后,而后再累加和数组中查找第一个大于随机数x的数字,使用二分查找法能够找到第一个大于随机数x的数字的坐标。而upper_bound() 函数恰好就是查找第一个大于目标值的数,lower_bound() 函数是找到第一个不小于目标值的数,用在这里就不太合适了,关于二分搜索发的分类能够参见博主以前的博客LeetCode Binary Search Summary 二分搜索法小结,参见代码以下:blog
解法二:ci
class Solution { public: Solution(vector<int> w) { sum = w; for (int i = 1; i < w.size(); ++i) { sum[i] = sum[i - 1] + w[i]; } } int pickIndex() { int x = rand() % sum.back(); return upper_bound(sum.begin(), sum.end(), x) - sum.begin(); } private: vector<int> sum; };
讨论:这题有个很好的follow up,就是说当weights数组常常变换怎么办,那么这就有涉及到求变化的区域和问题了,在博主以前的一道 Range Sum Query - Mutable 中有各类方法详细的讲解。只要把两道题的解法的精髓融合到一块儿就好了,能够参见论坛上的这个帖子。
相似题目:
Random Point in Non-overlapping Rectangles
参考资料:
https://leetcode.com/problems/random-pick-with-weight/discuss/154024/JAVA-8-lines-TreeMap