欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

最坏情况O(N) 求数组中第K 大的元素。

程序员文章站 2022-03-22 16:26:50
...
求数组中第K的元素的一般方法就是使用快速排序的划分,
Partion(seq,start,end) = p, 如果p=k 则ok。如果p >k 
则在start, p -1的区间里找第K大的数,Partion(seq,start,p-1)
否则partion(seq,p+1,end)
算法的平均时间复杂度为O(N),最坏情况为N^2,即每次划分把数组变为为(n-1) 和1的两断.
对此算法导论上给出了一个最坏情况下为O(N)的算法
该算法就是每次partion的时候找到一个好的划分,用中位数的中位数作为pivot。
把数组分为 N/5组,每组插入排序之后求得中位数,共 N/5 个中位数,对这N/5 中位数,使用同样过程再排序再求中位数(递归完成),最后求得N/5的中位数的中位数 作为最佳pivot,如下。
private static int  getPerfectPivot (int[]seq,int start,int end){
if(start==end){
return seq[start];
}
int groups = (end-start+1)%5==0?(end-start+1)/5:(end-start+1)/5+1;
int[] group_median = new int[groups];
for(int i=0; i < groups;i++){
int from = start+i*5;
int to = (from+5 < end)?(from+5):end;
for(int k=from+1;k<to;k++){
if(seq[k] < seq[k+1]){
//sawp
int temp = seq[k];
seq[k] = seq[k+1];
seq[k+1] =temp;
}
}
group_median[i] = seq[(from+to)/2];
}
return getPerfectPivot(group_median,0,groups-1);
}
 
稍微修改Partion的过程,即可获得O(N)复杂度的算法
private static int partionByMedian(int start,int end, int[]seq){
int pivot =  getPerfectPivot (seq,start,end);
//这里不再使用最后一个元素,也不使用rand随机设定一个元素
int swap_index = start -1;
for(int i = start;i<end;i++){
if(seq[i] <pivot){
swap_index++;
int temp = seq[swap_index];
seq[swap_index] = seq[i];
seq[i] = temp;
}
if(seq[i]==pivot && seq[end]!=pivot){
int temp = seq[i];
seq[i] = seq[end];
seq[end] = temp;
i--;
}
                //这一段是找出最好的pivot在数组中的位置并放到末尾。
}
swap_index++;
seq[end] = seq[swap_index];
seq[swap_index] = pivot;
return swap_index;
}
 
 
整个算法的执行时间可证明为O(N)参考算法导论。