以下是一个使用C++实现的 快速选择算法(Quickselect) 示例,用于在未排序数组中高效查找第k小的元素,基于分治策略并优化了快速排序的分区过程:
#include <iostream>
#include <vector>
#include <ctime>
using namespace std;
// 分区函数(Lomuto分区方案)
int partition(vector<int>& nums, int left, int right) {
int pivot = nums[right]; // 选择最后一个元素作为基准
int i = left - 1; // 指向小于基准的区域的右边界
for (int j = left; j < right; ++j) {
if (nums[j] <= pivot) {
swap(nums[++i], nums[j]); // 将较小元素交换到左侧
}
}
swap(nums[i+1], nums[right]); // 将基准放到正确位置
return i + 1; // 返回基准的最终位置
}
// 快速选择递归实现
int quickSelect(vector<int>& nums, int left, int right, int k) {
if (left == right) return nums[left]; // 基准情形
// 随机选择基准优化(避免最坏情况)
int random = left + rand() % (right - left);
swap(nums[random], nums[right]);
int pivotIndex = partition(nums, left, right);
if (k == pivotIndex) {
return nums[k];
} else if (k < pivotIndex) {
return quickSelect(nums, left, pivotIndex - 1, k);
} else {
return quickSelect(nums, pivotIndex + 1, right, k);
}
}
int main() {
srand(time(0)); // 初始化随机种子
vector<int> nums = {3, 2, 1, 5, 6, 4}; // 测试数组
int k = 2; // 查找第2小的元素(索引从0开始)
int result = quickSelect(nums, 0, nums.size() - 1, k);
cout << "数组元素:";
for (int num : nums) cout << num << " ";
cout << "\n第" << k+1 << "小的元素是:" << result << endl;
return 0;
}
算法核心思想演示(图书馆找书场景)
想象场景:图书馆有未排序的书堆(数组),需要快速找到第k薄的书(第k小元素),但不想花时间整理全部书籍
-
随机抽检
- 随便选一本书(随机基准)作为厚度参考
- 将更薄的书放左边,较厚的放右边(分区操作)
-
智能筛选
- 若目标书在左边区域:只关注左边
- 若在右边区域:只关注右边
- 重复直到找到目标
示例过程(k=2即第3小):
初始书堆:[3,2,1,5,6,4] → 随机选4号书(厚度6)
分区后:[3,2,1,5,4 |6] → 目标在左区
继续处理左区[3,2,1,5,4] → 随机选2号书(厚度1)
分区后:[1 |2,3,5,4] → 目标在右区
最终找到第3小元素:3
算法三步走
-
随机基准选择
swap(nums[random], nums[right])
避免最坏时间复杂度 -
分区重组
- 基准左侧全为更小元素
- 基准右侧全为更大元素
-
递归筛选
- 根据基准位置决定处理左/右分区
- 直到基准位置等于k索引
执行结果
数组元素:3 2 1 5 6 4
第3小的元素是:3
算法特性
特性 | 说明 |
---|---|
时间复杂度 | 平均O(n),最坏O(n²)(随机优化后概率极低) |
空间复杂度 | O(1) 原地操作 |
优势 | 比完全排序快,无需额外内存 |
适用场景 | 大数据量Top K问题、中位数计算 |
关键代码解析
-
随机基准选择
int random = left + rand() % (right - left); swap(nums[random], nums[right]);
通过随机化避免有序数组的最坏情况
-
递归筛选逻辑
if (k == pivotIndex) return nums[k]; // 找到目标 else if (k < pivotIndex) ... // 处理左区 else ... // 处理右区
-
分区优化
使用Lomuto分区方案,比Hoare方案更易理解
实际应用场景
- 成绩分析:快速找到年级第100名的分数
- 数据监控:实时获取传感器数据的中位数
- 金融系统:确定交易金额的百分位数
- 机器学习:选择特征值的中位数进行数据分割