洗牌算法
应用场景 Link
- 知道数组的长度N
- 将数组随机打散
算法实现
- 按照下标 i 从后向前遍历,在 [0, i] 随机选择一个下标 rand_idx
- 将 arr[rand_idx] 与 random_i[i] 进行交换
代码实现
void shuffle(vector<int>& arr) {for (int i = arr.size()-1; i >= 0; i--){int rand_idx = rand() % (i+1); // [0,i] 随机选择下标swap(arr[i], arr[rand_idx]); // 交换}
}
力扣
- 384 打乱数组
算法推理(见下图)
蓄水池抽样算法
应用场景:在长度为N的数据流中,等概率选取M个数 (N > M)
- N的长度未知,是一个数据流
算法实现
- 初始化蓄水池M:
- 将前M个元素,放入蓄水池 for (i=0; i<M; i++) pool.push(arr[i])
- 那么,蓄水池存放的下标范围: [0, M-1]
- 下标 i 从第M+1个元素,继续向后遍历
- 在 [0, i] 随机选择一个下标 rand_idx
- 如果 rand_idx 的下标在蓄水池存放的下标范围内,即:rand_idx ∈ [0, M-1],则:将当前元素random_i[i] 与 蓄水池中的元素 arr[rand_idx] 交换
代码实现
/** @brief 蓄水池抽样算法: 在数据流data[n]中, 等概率选出m个数据* @param [in] nums 数据流* @param [in] n 数据流中数据总个数* @param [in] m 采样数据总个数* @return 采样到m个数据*/
vector<int> Sampling(vector<int> nums, int n, int m) {// 前m个元素,构成蓄水池vector<int> pool;for (int i=0; i<m; i++) {pool.push_back(nums[i]);}// 后m个元素for (int i=m; i<n; i++) {int rand_idx = randIdx(0, i); // 返回[0,i]中的随机下标if (0 <= rand_idx < m) { // 如果idx是在蓄水池中,则交换swap(nums[i], nums[rand_idx]);}}// 蓄水池中的数据,就是等概率选出来的m个数return pool;
}
力扣
- 382链表随机节点
- 398 随机数索引
- 497 非重叠矩形中的随机点
- 519 随机翻转矩阵
算法推理(见下图)