-
LeetCode528 按权重随机选择
-
思路分析
-
使用前缀和技巧
加入我输入的权重数组w = [1, 3, 2, 1],则让概率符合权重,可以抽象为如下线段
index 0 1 2 3
w 1 3 2 1
preSum 1 4 6 7
如果我随机生成一个[1, 7]之间的整数,该数落在哪个颜色的线段上,就选择该颜色对应的权重索引,例如:若生成了target = 5,target落在蓝色线段上,则返回索引2
-
使用左侧边界的二分查找算法:因为preSum中并不存在target = 5,这样我们应该选择比5大的最小元素,也就是6,即preSum数组索引2,因此要使用左侧边界的二分查找算法
-
关于目标元素target不存在于数组nums中时
以左侧边界的二分搜索的返回值为例,可以做以下解读
- 返回的这个值是nums中大于等于target的最小元素的索引
- 返回的这个值是target应该插入在nums中的索引位置
- 返回的这个值是nums中小于target的元素个数
-
-
以上三种解读等价
2. 代码实现
```
class Solution {
private int[] preSum;
private Random random = new Random();
public Solution(int[] w) {
int len = w.length;
preSum = new int[len];
preSum[0] = w[0];
for (int i = 1; i < len; i++) {
//前缀和数组下标从0开始
preSum[i] = preSum[i - 1] + w[i];
}
}
public int pickIndex() {
int preLen = preSum.length;
//保证取到[1, preSum[prelen - 1]
int target = random.nextInt(preSum[preLen - 1]) + 1;
return leftBound(target);
}
int leftBound(int target) {
if (preSum.length == 0) return -1;
int left = 0;
int right = preSum.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (preSum[mid] == target) {
right = mid - 1;
} else if (preSum[mid] > target) {
right = mid - 1;
} else if (preSum[mid] < target) {
left = mid + 1;
}
}
return left;
}
}
/**
* Your Solution object will be instantiated and called as such:
* Solution obj = new Solution(w);
* int param_1 = obj.pickIndex();
*/
```