问题描述:给定一系列数{a1,a2,...,an},这些数无序的,现在求第k大的数。
看到这个问题,首先想到的是先排序,然后直接输出第k大的数,于是得到啦基于排序的算法
算法一:
#include<iostream>
#include<algorithm>
using namespace std;
bool cmp(int a, int b){ return a > b; }
int main(){
int k;
int a[9] = { 6, 5, 9, 8, 2, 1, 7, 3, 4 };
cout << "Input k:";
cin >> k;
sort(a, a + 9, cmp);
cout << a[k - 1] << endl;
return 0;
}
算法耗时主要在排序上,于是这个算法复杂度是O(nlogn);
算法二:
算法二是基于分解的方法,问题求解的是第k大的数,若果我将这系列数全部排序肯定会做太多无用功,比如我只需找出第k大,我可以先找出1大,第2大....第k大,在结束,此时时间为O(kn),当k比较少时候算法复杂度为0(n),若当k是关于n的函数时候算法复杂度为O(n2),最好的情况是k=1和k=n,最坏的情况是k=n/2,O(n)=<f(n)<=O(n2/2);具体代码如下:
#include<iostream>
using namespace std;
#include<cmath>
#define m int(pow(-2,31)); /*将m赋值为最小的整数*/
int Find_Kth(int a[], int n, int k);
int main(){
int k;
int a[9] = { 6, 5, 9, 8, 2, 1, 7, 3, 4 };
cout << "Input k:";
while (cin >> k){
cout << Find_Kth(a, 9, k) << endl;
}
return 0;
}
int Find_Kth(int a[], int n, int k){
int i,j, redex=0, NewMax = a[0];
for (i = 0; i < k; i++){ /*每次采用打擂台方法找到本次最大值后赋值为m*/
for (i = 0; i < n; i++)
if (a[i]>NewMax){
NewMax = a[i];
redex = i;
}
a[redex] = m;
}
return NewMax;
}
算法二有局限性,复杂度取决于k值,下面还是采用基于分解的思想,我们需要的是求第k大的数,于是可以采用快速排序的思想,任意选择一个数key将这序列数分解为s1和s2两个序列,其中s1中的数全部小于key,而s2中的数全部大于或等于key(key是s2中的最小的数),设s1和s2中元素个数为|s1|和|s2|,如果k==|s2|,那摩说明第k大的数恰好为key,如果k<|s2|,那么问题转化为求|s2|中第k大的数,如果k>|s2|,那么问题转化为求s1中第k-|s2|大的数,这样所有的过程可以通过递归来实现,于是有啦算法三。
算法三:
#include<iostream>
using namespace std;
int Find_Kth(int *a,int left,int right,int k);
int main(){
int k;
int a[9] = {6,5,9,8,2,1,7,3,4};
cout << "Input k:";
cin >> k;
cout << Find_Kth(a, 0, 8, k) << endl; /*输出第k大的数*/
return 0;
}
int Find_Kth(int *a,int left,int right, int k){
int key = a[left]; /*设定关键值*/
int low = left,high=right;
while (high>low){ /*类似于快速排序*/
while (a[high] >= key&&high>low)
high--;
a[low] = a[high];
while (a[low] <= key&&high>low)
a[low++];
a[high] = a[low];
}/*循环跳出必定有high=low*/
a[low] = key;
int r = right - low + 1; /*右端元素个数*/
if (k== r)
return a[low]; //恰好a[low]为第k大值
else if (k < r)
return Find_Kth(a, low+1, right, k); //第k大的值在a[low]右边第k大值
else
return Find_Kth(a, left, low - 1, k - r); /*第k大值在a[low]右边序列的第r-k大的值*/
}
事实上,C++的STl早已经为我们提供啦函数nth_element,包含头文件#include<iostream>中.nth_element有四个参数,第一个和第三个为begin和end的地址,表示搜索范围是区间[begin,end)里面的数,第二个参数为找的第k个数的地址,第四个参数为cmp函数,函数可以看成 void element(int*begin,int*Nth,int*end,boo(*pdrf)(int a,int b));
#include<iostream>
#include<algorithm>
using namespace std;
bool cmp(int a, int b){
return a > b;
}
int main(){
int k;
int a[9] = { 6, 5, 9, 8, 2, 1, 7, 3, 4 };
cout << "Input k:";
while (cin >> k){
nth_element(a, a + k-1,a+9, cmp); //将第k大的数放在k-1位置
cout << a[k - 1] << endl;
}
return 0;
}