markdown复制## 1. 问题背景与核心需求
快速选择算法是算法竞赛和面试中的高频考点,AcWing 786题要求在一个无序数组中找出第k小的数。常规排序后取第k个元素的方法时间复杂度为O(nlogn),而快速选择算法可以优化到平均O(n)时间复杂度,这对处理大规模数据至关重要。
我在刷题过程中发现,很多同学虽然能写出快速排序,却对快速选择算法的变体束手无策。本文将拆解这个经典问题的三种实现方案,并分享调试时发现的边界陷阱。
## 2. 算法核心思想解析
### 2.1 快速选择与快速排序的异同
快速选择算法脱胎于快速排序的partition过程:
- 相同点:都采用分治思想,通过pivot划分数组
- 不同点:快速排序递归处理两侧子数组,而快速选择只需处理包含第k元素的单侧
关键优化在于每次partition后:
1. 计算左区间元素个数sl
2. 若k ≤ sl,只在左区间递归
3. 否则在右区间寻找第(k-sl)小的数
### 2.2 时间复杂度证明
理想情况下每次partition将数组减半:
T(n) = T(n/2) + O(n)
根据主定理可得O(n)
最坏情况(如已排序数组):
T(n) = T(n-1) + O(n) → O(n²)
可通过随机化pivot避免(后文详述)
## 3. 三种实现方案对比
### 3.1 基础版(Lomuto Partition)
```cpp
int quick_select(int q[], int l, int r, int k) {
if (l >= r) return q[l];
int i = l - 1, j = r + 1, x = q[l + r >> 1];
while (i < j) {
do i++; while (q[i] < x);
do j--; while (q[j] > x);
if (i < j) swap(q[i], q[j]);
}
if (k <= j) return quick_select(q, l, j, k);
else return quick_select(q, j + 1, r, k);
}
注意:这种写法在元素全相同时会
