一:
线性时间选择中,最坏情况下时间复杂度为O(n^2) , 但如果在线性时间内找到一个划分基准,使得按照这个基准所划分的两个子数组的长度至少为原数组的k倍( 0<k<1)。
二:
(1) 将n个输入的元素分成 (n-4)/5组,每一组都是5个元素,可能最后一个不是,用任意的排序算法将每组的5个元素排好,然后取出5个元素的中位数。
(2)递归调用Select函数(见程序)找出这(n-4)/5个中位数数的中位数,然后将这个中位数作为划分基准,作为快速排序中的参考值进行排序。
三:
中位数的中位数x作为划分基准时,可以保证图中左上角的部分比x小,图中右下角的部分比x大。所以通过x作为划分基准可以保证大约四分之一的数据比x小,四分之一的数组比x大。不会产生最后情况,该复杂度约为O(N) .
如图所示:.
四:
程序如下:
#include <iostream>
#include <algorithm>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#define LENGTH 10
using namespace std;
template <class Type>
void swap(Type *a,Type *b){
Type c;
c = *a;
*a = *b;
*b = c;
}
template <class Type>
int Partition(Type a[],int p,int r,Type x){ //快排过程中参考值的下标
int sl = p-1;
int sr = r+1;
while(true){
while(a[++sl] <= x && sl <= r);
while(a[--sr] > x && sr >= 0);
if(sl > sr)break;
swap(a[sl],a[sr]);
}
swap(a[p],a[sr]);
return sr;
}
template <class Type>
Type Select(Type a[],int p,int r,int n){
if(r - p < 75){ //如果数组大小小于75 ,就通过普通排序给出
sort(&a[p],&a[r] + 1);
return a[p+n-1];
}else{
for(int i = 0;i <= (r-p-4)/5;i++){ //分为 (n-4)/5组
sort(&a[p + i*5],&a[p + i*5+4] +1); //对每一组的5个元素排序
swap(a[p+i*5+2],a[p+i]); //将中位数交换到a[p+i]
}
Type x = Select(a,p,p+(r-p-4)/5, (r-p-4)/10); //因为中位数交换到a[p+i],所以前(r-p-4)/5个元素都是中位数,对这(r-p-4)/5个元素递归,找到中位数的中位数 x
int q = Partition(a,p,r,x); //中位数的中位数x 作为参考值,进行快排。
int s = q - p + 1;
if(s == n )return x;
else if(s < n) return Select(a,q+1,r,n - s);
else return Select(a,p,q-1,n);
}
}
int main(){
int n;
srand(time(NULL));n= rand()%LENGTH + 1;
int a[LENGTH] = { 2,1,3,4,7,5,6,9,8,0};
cout<<"第"<<n<<"小的数是:";
cout<<Select(a,0,LENGTH-1,n);
}