本题是上一题的拓展算法,快速选择,顾名思义,就是快速选择数组中第 $k$ 小的数。
首先我们先来回忆一下 快速排序。
您当然可以排序一遍然后输出 $q[k]$
#include <iostream>
using namespace std;
const int N = 100010;
int n, k, q[N];
void quick_sort(int l, int r){
if (l == r) return;
int x = q[l + r >> 1], i = l - 1, j = 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]);
}
quick_sort(l, j);
quick_sort(j + 1, r);
}
int main()
{
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i ++)
scanf("%d", &q[i]);
quick_sort(1, n);
printf("%d", q[k]);
return 0;
}
但是这样太慢了($O(logn)$),所以我们可以优化一下。
优化
我们把目光聚焦在递归处理左右区间这一步,我们可以计算出左区间的长度,一个结论是,如果 $k$ 的长度 $\leq k$ 的话 ,则右区间一定不会是结果;反之,如果$k$ 严格大于左区间长度的话,左区间一定不会是结果。
代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
const int N = 100005;
int n, k;
int q[N];
int quick_sort(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]);
}
int sl = j - l + 1;
if (k <= sl)
return quick_sort(l, j, k);
else
return quick_sort(j + 1, r, k - sl);
}
int main()
{
scanf("%d%d", &n, &k);
for (int i = 0; i < n; i ++)
scanf("%d", &q[i]);
printf("%d\n", quick_sort(0, n - 1, k));
return 0;
}
该算法的时间复杂度是 $O(n)$