问题:给定n个整数,如何用最快的方法求出第k大的数?
我们当然可以对这n个整数排序然后直接输出第k大的数,时间复杂度为O(nlog n)。
但是实际上我们可以用基于快速排序所用到的思想,在每一层递归中,随机选取一个数为基准值,把比它大的数交换到左半段,把其余的数和基准值自身一起作为右半段。在这个过程中假设统计出比基准值大的数有cnt个,若k<=cnt,就在左半段中递归寻找第k大;若k>cnt,就在右半段中递归寻找第k-cnt大的数。递归边界为这一段区间长度为1。就返回这个数即为最初所找区间第k大的数。
因为是随机选取基准值,所以找第k大的数平均时间复杂度为n + n/2 + n/4 + ... + 1 = O(n)。
总的时间复杂度即为O(n)
实现代码:
#include<bits/stdc++.h>
using namespace std;
int a[100010];
void swap(int& x, int& y) {
int temp = x;
x = y;
y = temp;
}
int random(int l, int r) {//返回一个在[l,r]范围内的随机整数
return rand() % (r - l + 1) + l;
}
int find_kth(int l, int r, int k) {
if (l == r)return a[l];//递归边界
int temp = a[random(l, r)];//随机选取主元
int cnt = 0;//计比主元大的个数
int i = l, j = r;
while (i <= j) {
while (a[i] > temp && i <= j)i++, cnt++;
while (a[j] <= temp && i <= j)j--;
if (i < j) {
swap(a[i], a[j]);
cnt++;
}
i++, j--;
}
if (cnt >= k) {//若k<=cnt,就在左半段中递归寻找第k大
return find_kth(l, l + cnt - 1, k);
}
else {//否则在就在右半段中递归寻找第k-cnt大的数
return find_kth(l + cnt, r, k - cnt);
}
}
int main() {
int n; cin >> n;
for (int i = 1; i <= n; i++)cin >> a[i];
int k; cin >> k;
cout << find_kth(1, n, k) << endl;
}

京公网安备 11010502036488号