一、以期望线性时间做选择
一般来说,中位数的查找算法都是基于先排序,后找中间位置的数字的算法,但是因为线性时间排序所收到的限制比较大,而如果使用基于比较的排序,时间复杂度将至少为O(nlogn),如何以线性时间完成中位数或者数组中第N大元素的查找呢?
快速排序算法在每一次局部递归后都保证某个元素左侧的元素都比他小,右侧的元素都比她大,因此,可以利用这个思路快速找到第N大元素,而与快速排序算法不同的是,我们关注的并不是元素的左右两边,而仅仅是某一边,这样,可以证明,算法是线性时间复杂度的
二、程序代码
/*
* file: main.c
* author: 龙泉居士
* date: 2013-01-09 19:14
*/
#include <stdio.h>
void exch (int *array, int i, int j)
{
if (i!=j)
{
array[i] ^= array[j];
array[j] = array[i] ^ array[j];
array[i] ^= array[j];
}
}
double select_middle (int *array, int beg, int end, int n)
{
if (n == 1)
return array[1];
int i = beg, j;
for (j=i+1; j<=end; ++j)
if (array[j]<=array[beg])
{
++i;
exch (array, i, j);
}
exch (array, beg, i);
if (i < n/2)
return select_middle (array, i+1, end, n);
else if (i > n/2)
return select_middle (array, beg, i-1, n);
else
{
if (n%2)
return array[i];
else
{
int j, m=array[0];
for (j=1; j<i; ++j)
if (array[j] > m)
m=array[j];
return (double)(array[i]+m)/2;
}
}
}
int main ()
{
int n=0;
int array[256];
printf ("Please input numbers, end by 9999:\n");
while (1)
{
scanf ("%d", &array[n]);
if (array[n] == 9999)
break;
++n;
}
printf ("%lf\n", select_middle(array, 0, n-1, n));
return 0;
}
三、改进
在特殊情况下(完全逆序),这个算法的时间复杂度将不会是线性的,这是什么他原因造成的呢?这是因为在递归过程中,每一次选择的基准元素都是首个元素,即使使用随机化的算法,依然很有可能选择以非常大或者非常小的元素作为基准
解决方法是,先对整个数组划分为若干小组,再对每个小组进行排序,选择出中位数,再在已经选出的N个中位数中找出他们的中位数,这个最终的中位数就是十分接近中间的数字,以这个数为基准的话,就可以保证在最坏情况下,算法依然以线性时间运行