算法设计与分析——分治法求最大值和最小值
Description
给定n个数,在最坏情况下用 3n/2-2 次比较找出这n个数中元素的最大值和最小值。
要求只编写函数
void maxmin(int a[],int low,int high,int *max,int *min).
系统会自动在程序的最后加上如下代码:
int main() { int max,min,k,a[200]; int m; while(scanf("%d",&k)&&k) { for(m=0;m<k;m++) scanf("%d",&a[m]); maxmin(a,0,k-1,&max,&min); printf("%d %d\n",max,min); } }
Input
包含多组测试数据。每组测试数据的第一个元素是整数的个数n,接下来是n个整数。0表示结束。 n<=200
Output
这n个数中的最大值和最小值。
Sample Input
5 1 8 2 4 3 3 2 4 1 0
Sample Output
8 1 4 1
如果之前遍历一遍需要比较n-1次,才能得到数组中的最大值和最小值,这显然是不符合预期要求的,这里应该采用的是分治思想。
分治法主要有三个步骤:
1.分解:将原问题划分为规模较小的几个子问题。
2.求解:子问题小到可以求解了,就去求解。
3.合并:将子问题的解合并为原问题的解。
对于本问题就是不断地进行二分,直到分解为不可分的一个数组元素,这个数组元素是最小子问题的最大值也是最小值,之后再逐一合并为原问题的解。
#include<cstdio> #include<algorithm> #define inf 0x3f3f3f3f using namespace std; void maxmin(int *a,int left,int right,int *max,int *min) { int mid; int lmax=0,lmin=inf,rmax=0,rmin=inf; if(left==right) { *max=a[left]; *min=a[right]; return ; } mid=(left+right)/2; maxmin(a,left,mid,&lmax,&lmin); maxmin(a,mid+1,right,&rmax,&rmin); if(lmax>rmax) { *max=lmax; } else { *max=rmax; } if(lmin<rmin) { *min=lmin; } else { *min=rmin; } return ; } int main() { int max,min,k,a[200]; int m; while(scanf("%d",&k)&&k) { for(m=0; m<k; m++) scanf("%d",&a[m]); maxmin(a,0,k-1,&max,&min); printf("%d %d\n",max,min); } }
使用分治法解决了本问题,但是3n/2-2 次比较是怎么计算得到的呢?
我们来分析一下本算法:
void maxmin(int *a,int left,int right,int *max,int *min) { int mid; int lmax=0,lmin=inf,rmax=0,rmin=inf; if(left==right) { *max=a[left]; *min=a[right]; return ; }// ------------------------------------------------>O(1) mid=(left+right)/2;//------------------------------->O(1) maxmin(a,left,mid,&lmax,&lmin);//------------------->T(n/2) maxmin(a,mid+1,right,&rmax,&rmin);//---------------->T(n/2) if(lmax>rmax) { *max=lmax; } else { *max=rmax; } if(lmin<rmin) { *min=lmin; } else { *min=rmin; } return ;//------------------------------------------>O(1) }
可以得到算法的递推表达式,进而求解。