分治与递归
1.问题描述
编写程序,实现线性时间内选择n个元素的中位数的算法。并对于不同的n,测试平均时间效率。
2.问题分析
要求线性时间内选择n个元素的中位数,可以采用分治算法,将 n个元素划分为俩个子数组,判断中位数在哪一个子数组,然后只对该划分出的子数组进行递归处理,从而递归找出中位数。
3.算法设计
分治法的设计思想是:将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。
分治策略是:对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小)则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。这种算法设计策略叫做分治法。
如果原问题可分割成k个子问题,1<k≤n,且这些子问题都可解并可利用这些子问题的解求出原问题的解,那么这种分治法就是可行的。由分治法产生的子问题往往是原问题的较小模式,这就为使用递归技术提供了方便。在这种情况下,反复应用分治手段,可以使子问题与原问题类型一致而其规模却不断缩小,最终使子问题缩小到很容易直接求出其解。这自然导致递归过程的产生。分治与递归像一对孪生兄弟,经常同时应用在算法设计之中,并由此产生许多高效算法。
给出n个元素放在数组a[0,n-1]中,对于此数组中任意一个子数组a[p,r],从中随机选择一个a[i],依次为划分基准,将a[p,r]划分为a[p,q-1]和a[q+1,r],其中a[p,q+1]中的元素全部小于aq,a[q+1,r]中的元素全部大于a[q],a[p,r]中第k小元素当k<=q-p+1时,在a[p,q]中找第k小元素,当k>q-p+1时,则在a[q+1,r]中找第k-(q-p+1)小元素,递归调用直到要寻找的子数组中只剩一个元素时,则找到了中位数,返回这个数。 算法的时间效率分析如下: 在最坏情况下:
解此递归方程可得T(n)=O() 在最好情况下:
解此递归方程可得T(n)=O(n) 可以证明在平均情况下T(n)=O(n),满足题目要求在线性时间内找到中位数
4.算法实现
#include<stdio.h>
#include<stdlib.h>
int partition(int* a,int p,int r)
{
int i,j,x,temp;
i=p;
j=r+1;
x=a[p];
while(1)
{
while(a[++i]<=x&&i<r);
while(a[--j]>=x);
if(i>=j)
break;
else
{
temp=a[i];
a[i]=a[j];
a[j]=temp;
}
}
a[p]=a[j];
a[j]=x;
return j;
};
int randompartition(int* a,int p,int r)
{
int i,temp;
i=(p+r)/2;
temp=a[p];
a[p]=a[i];
a[i]=temp;
return partition(a,p,r);
};
int randomselect(int* a,int p,int r,int k)
{
if(p==r)
return a[p];
int q,j;
q=randompartition(a,p,r);
j=q-p+1;
if(k==j)
return a[q];
if(k<j)
return randomselect(a,p,q-1,k);
else
return randomselect(a,q+1,r,k-j);
};
int main(void)
{
int n,i,result;
printf("输入元素个数");
scanf("%d",&n);
int*a=new int[n];
for(i=0;i<n;i++)
scanf("%d",&a[i]);
result=randomselect(a,0,n-1,(n+1)/2);
printf("中位数是%d\n",result);
delete a;
return 0;
}