第K大数的问题
就是求解一个序列中第K大的数
常见做法:将原序列排序,然后访问第n - k个数即可,总体时间复杂度O(nlogn)
而本题其实只需要求第K大数,而对于其余的数是否有序,我们并不关心,
因此可以利用快速排序,在线性的O(n)时间内获得第K大数
先回顾一下快速排序的算法思想:
1. 在原序列中随机找一个中心值pivot,将小于中心值的元素放在其左边,大于中心值的元素放在其右边
一次快排,中心值位置就唯一确定了
2.对左右两边进行同样的操作
#include
#include
#include //rand()
#include//swap()
using namespace std;
const int MAXN = 1000 + 10;
int arr[MAXN];
int Partition(int left, int right){
//将原始的无序序列找一个值作为中心值
//并且将他的位置固定下来
//即将原始序列中小于中心值的数放在它左边,大于放右边
int random = left + rand() % (right - left + 1);
//rand()会随机生成一个值,之后对于()取模,能保证值在0~right-left
//+left能保证值在left到right之间,并且这两个值也能取到
swap(arr[left],arr[random]);
while(left < right){//自己想象那个数组和指针
while(left < right && arr[left] <= arr[right]){//此时中心值在左边,
//指针从右往左找到第一个小于中心值的
right--;
}
swap(arr[left],arr[right]);
while(left < right && arr[left] <= arr[right]){
left++;
}
swap(arr[left],arr[right]);
}
return left;//此时 left == right
}
void QuickSort(int left,int right){ //快排的参数是待排序的两个端点
//当只有一个元素存在的情况下,整个序列是有序的
//因此只处理多个元素存在的序列
if(left < right){
int position = Partition(left,right); //先获取中心值的位置
QuickSort(left,position -1);
QuickSort(position +1,right);
}
}
int main(){
int n;
scanf("%d",&n);
for(int i = 0; i < n; ++i){
scanf("%d",&arr[i]);
}
QuickSort(0, n - 1);
for(int i = 0; i < n; ++i){
printf("%d ",arr[i]);
}
return 0;
}
如何利用快速排序高效地求得第K大数呢?
在快排过程中,每一步都会将随机选择出来的中心值的位置唯一确定
第K大数就是原始序列在有序(从小到大)的情况下的下标index = n - k 的数
所以在快排过程中,你确定的中心值的下标正好是 n - k ,那么这个中心值正是第K大数
而如何一步步筛选得到最终结果呢?
若此时中心值下标 < n - k,而由于快排的特性,左边都比中心值小,右边都比中心值大,
那么第K大数就一定在此时的中心值的右边,那么下一次的随机取值,就从右边取了!这样可以缩小搜索空间
时间复杂度为 n + n/2 + n/4 +......+1 = O(n) < O(nlogn)
#include
#include
#include //rand()
#include//swap()
using namespace std;
const int MAXN = 1000 + 10;
int arr[MAXN];
int Partition(int left, int right){
//将原始的无序序列找一个值作为中心值
//并且将他的位置固定下来
//即将原始序列中小于中心值的数放在它左边,大于放右边
int random = left + rand() % (right - left + 1);
//rand()会随机生成一个值,之后对于()取模,能保证值在0~right-left
//+left能保证值在left到right之间,并且这两个值也能取到
swap(arr[left],arr[random]);
while(left < right){//自己想象那个数组和指针
while(left < right && arr[left] <= arr[right]){//此时中心值在左边,
//指针从右往左找到第一个小于中心值的
right--;
}
swap(arr[left],arr[right]);
while(left < right && arr[left] <= arr[right]){
left++;
}
swap(arr[left],arr[right]);
}
return left;//此时 left == right
}
int QuickSort(int left,int right,int p){ //快排的参数是待排序的两个端点
//当只有一个元素存在的情况下,整个序列是有序的
//因此只处理多个元素存在的序列
//p是目标位置
int position = Partition(left,right); //先获取中心值的位置
if(position < p){
return QuickSort(position + 1,right, p);
}else if ( position > p){
return QuickSort(left,position - 1, p);
}else{
return arr[p];
}
}
int main(){
int n;
scanf("%d",&n);
int k;
scanf("%d",&k);
for(int i = 0; i < n; ++i){
scanf("%d",&arr[i]);
}
int answer = QuickSort(0, n - 1, n - k);
printf("%d\n",answer);
return 0;
}
主要修改了quicksort函数的代码,p是目标位置。