分治与递归

183 阅读2分钟

分治与递归

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)小元素,递归调用直到要寻找的子数组中只剩一个元素时,则找到了中位数,返回这个数。 算法的时间效率分析如下: 在最坏情况下:

image.png

解此递归方程可得T(n)=O() 在最好情况下:

image.png

解此递归方程可得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;
}

运行结果

image.png

image.png