问题描述:在含有n个不同元素的集合a[n]中同时找出它的最大值和最小值。不妨设n=2m次方,m>=0。
分治法
基本思想:将一个规模为n的问题分解为k个规模较小的子问题,这些子问题相互独立且与原问题相同。递归的解决这些子问题,然后再将这些子问题合并得到原问题的解。
Divide-and-Conquer(P)
{
if(|P|<=n0)Adboc(P);
divide P into smaller subinstances
P1,P2,...,Pk;
for(i=1;i<=k;i++)
yi=Divide-and-Conquer(Pi);
return Merge(y1,y2,...yk);
}
分治法的基本框架:
其中,|P|是问题P的规模,n0为一阙值,表示当问题P的规模不超过n0时,问题已经容易被解决,不需要继续被分解。Adhoc§是该分治法中的基本子算法,用于直接解决最小的子问题。Merge(y1,y2,…yk)是该分治法中的合并子算法,用于合并P的子问题。
不难发现,实现分治法的保障是递归,如何构建好递归非常重要,递归的确定需要确定两个部分,分别为递归边界和递归方法。回到本问题,问题需要我们输入问题规模n和问题的具体数字(用整型数组a来存储)。
例:问题规模n=8,具体数字a[]={22,10,60,78,45,51,8,36}。通过对分治法和递归算法的了解我们不难构建出问题函数的过程图。
递归边界:
SL和SR是左右子问题返回的最大值与最小值的结构体
if(i==j)
{
s.max=s.min=b[i];
return s;
}
if(i==j-1)
{
if(b[i]>=b[j])
{
s.max=b[i];
s.min=b[j];
return s;
}
if(b[i]
递归函数:
SL=MAXMIN(i,mid,b);
SR=MAXMIN(mid+1,j,b);
合并:
if(SL.max>SR.max)
s.max=SL.max;
else
s.max=SR.max;
if(SL.min>SR.min)
s.min=SR.min;
else
s.min=SL.min;
return s;
注意:
1(递归边界).我们在设计算法的时候,根据算法中的递归边界可以确定需要确定子问题中的最左边标志i 和最右边标志j ,在划分子问题的时候我们还得定义一个中间变量mid 作为左子问题的最右边界和mid+1作为右子问题的最左边界。
2(递归函数).试想,我们把一组数据分为左右两个子问题,每个子问题都需要求出一个最大值和最小值返回,函数返回的时候,为了减少算法的复杂程度,我们可以用C语言当中的结构体变量,定义一个结构体变量S,其中包含两个整型变量最大值max和最小值min。
3(合并算法).在合并子问题的时候,左右子问题都会产生各自的最大值和最小值,左子问题的最大值SL.max最小值SL.min,右子问题的最大值SR.max最小值SR.min。两边的最大值比较,最大的就是该问题的最大值max,两边的最小值比较,最小值就为该问题的最小值min。
代码如下
#include
#include
typedef struct
{
int max;
int min;
}S;
S MAXMIN(int i,int j,int b[]){
S s,SL,SR;
int mid;
if(i==j)
{
s.max=s.min=b[i];
return s;
}
if(i==j-1)
{
if(b[i]>=b[j])
{
s.max=b[i];
s.min=b[j];
return s;
}
if(b[i]SR.max)
s.max=SL.max;
else
s.max=SR.max;
if(SL.min>SR.min)
s.min=SR.min;
else
s.min=SL.min;
return s;
}
void main(){
int k,n,a[100];
S d;
printf("输入问题规模n:");
scanf("%d",&n);
printf("输入问题的数字:");
for(k=0;k<=n-1;k++)
{
scanf("%d",&a[k]);
}
d=MAXMIN(0,n-1,a);
printf("最大值为:%d,最小值为:%d\n",d.max,d.min);
system("pause");
}