Question
给定一个长度为 n
的整数数列,以及一个整数 k
,请用快速选择算法求出数列从小到大排序后的第 k
个数。
输入格式
第一行包含两个整数 n
和 k
。
第二行包含 n
个整数(所有整数均在 1∼109
范围内),表示整数数列。
输出格式
输出一个整数,表示数列的第 k
小数。
数据范围
1≤n≤100000
,
1≤k≤n
输入样例:
5 3
2 4 1 5 3
输出样例:
3
Ideas
Code
// 快排步骤(O(nlgn)):
// 1.寻找分界点x,a[l + r >> 1]
// 2.划分区间,使得左边均<=x,右边均>=x
// 3.递归左右两边
// 快速搜索步骤(O(n))
// 当进行到第2步时,左区间严格<=右区间,所以第k小的数要么在左区间,要么在右区间,
// 只需要递归一边即可,这由k与左区间的元素个数有关
#include <iostream>
using namespace std;
const int N = 1E5 + 10;
int a[N];
int quick_choose(int *a, const int& l, const int& r, const int& k)
{
if (l >= r) return a[l];
int x = a[l + r >> 1];
int i = l - 1, j = r + 1;
while(i < j)
{
do i ++; while(a[i] < x); // 快排左边寻找a[i] >= x
do j --; while(a[j] > x);
if (i < j) swap(a[i], a[j]);
}
int sl = j - l + 1;
if (k <= sl)
return quick_choose(a, l, j, k); // 左边区间的数目
else
return quick_choose(a,j + 1, r, k - sl);
}
int main()
{
int n, k;
scanf("%d%d", &n, &k);
for (int i = 0; i < n; i ++) scanf("%d", &a[i]);
cout << quick_choose(a, 0, n - 1, k) << endl;
return 0;
}