递归与分治策略之利用中位数线性时间选择

时间:2021-11-09 17:17:25

前言

这一篇文章就上上一篇博文算法的进一步优化了!
这里我们就利用中位数来进行线性时间的选择算法!

中位数就是指将数据按大小顺序排列起来,形成一个数列,居于数列中间位置的那个数据就是中位数。

算法思路

(1)将输入的n个数划分成 n5 个组,当然最后一组的数目可能是小于5的!
(2)用任意的排序方法对他们进行排序,并取出一共 n5 个中位数。
(3)找出该 n5 个中位数中的中位数。(如果 n5 是偶数则取相对大的那个数)
(4)将全部的数划分为两个部分,小于基准的在左边,大于等于基准的放右边。

我们用小圆点表示元素,得到如下图:
递归与分治策略之利用中位数线性时间选择

说明:
图中中间白色圈表示各组数据的中位数,最中间灰色表示中位数的中位数! 箭头是从较小的数指向较大的数!

故我们可以使用该数作为划分的基准(比上一个随机基准的方法会好很多)!

递归与分治策略之利用中位数线性时间选择

图中 3A1=3(n5)10
当n≥75时,3A1大于等于 14n 。所以按此基准划分所得的左右2个子数组的长度都至少缩短 14

代码描述

int Select(int a[],int p,int r,int k)   
{
if(r-p<75)
{
//这里对数组 a[p->r] 进行排序
return a[p+k-1];
}
//(r-p-4)/5相当于n-5
for(int i=0; i<=(r-p-4)/5; i++)
{
//这里将元素每5个分成一组,分别排序,并将该组中位数与a[p+i]交换位置
//使所有中位数都排列在数组最左侧,以便进一步查找中位数的中位数
}

int x = Select(a,p,p+(r-p-4)/5,(r-p-4)/10); //找中位数的中位数
int i = Partition(a,p,r,x); //以x为基准对数组a进行划分

int j = i-p+1;
if(k<=j)
{
return Select(a,p,i,k);
}
else
{
return Select(a,i+1,r,k-j);
}
}