原题链接:AcWing 786.第k个数
给定一个长度为 n 的整数数列,以及一个整数 k,请用快速选择算法求出数列从小到大排序后的第 k 个数。
输入格式
第一行包含两个整数 n 和 k。
第二行包含 n 个整数(所有整数均在 1∼109 范围内),表示整数数列。
输出格式
输出一个整数,表示数列的第 k 小数。
数据范围
1≤n≤100000,
1≤k≤n
输入样例:
5 3
2 4 1 5 3
输出样例:
3
思路:
快速排序能够将整个序列排序,但时间复杂度是O(nlogn),如果只需要第K个小的数,是没必要将整个序列排序的
此时就用 快速选择算法,时间复杂度O(n)
和快速排序一样,也是划分,递归,区别在于快速选择根据情况 只递归左右两个区间中的一个
- 如果左边区间元素个数大于k,说明目标元素在左边,只需要递归左子区间
- 反之,递归右子区间
c++代码:
#include <iostream>
using namespace std;
const int maxn = 1e5 + 10;
int n, k, a[maxn];
// 快速选择算法 输出第k个小的数
int quick_sort(int a[], int l, int r, int k){
if(l == r) return a[r];
int pivot = a[l], i = l - 1, j = r + 1;
while(i < j){
do(++i); while(a[i] < pivot);
do(--j); while(a[j] > pivot);
if(i < j) swap(a[i], a[j]);
}
// 左半区间的元素个数 根据sl和k的关系, 可以确定递归左半区间或者右半区间
int sl = j - l + 1;
if(k <= sl) return quick_sort(a, l, j, k);
else return quick_sort(a, j + 1, r, k - sl);
}
int main(){
cin >> n >> k;
for(int i = 0; i < n; i++ )
cin >> a[i];
cout << quick_sort(a, 0, n - 1, k) <<endl;
return 0;
}
复杂度分析
- 时间复杂度:O(n),需要划分的区间的元素个数:n + n/2 + n/3+…… < 2n,因此是O(n)
- 空间复杂度:O(1)