什么是二分法
在查找有序数组中的某个元素的时候,我们往往就会首先想到二分法。比较目标数和数组的中间数的大小,然后将数组中的数据不断分为两份,从而判断目标数据应该处于数组的左半段还是右半段。这种二分法查找的时间复杂度为O(logN)。故而一般在有序数组中我们常会考虑二分法。
但是二分法并不是只能使用在有序数组中,求解某些特殊问题如求解一个数组上的局部极小值也可以使用二分法。还有我们今天想要讨论的求解数组中的极大值也可以使用二分法来进行求解。
图解二分法
二分法求最大值代码如下:
int process(int* a,int L,int R){
//L是该段数据的左边界
//R是该段数据的右边界
if(L == R)
return *(a+L);
int mid = L+((R-L)/2);//计算中点
int Leftmax = process(a,L,mid);
int Rightmax = process(a,mid+1,R);
return (Leftmax>Rightmax)?Leftmax:Rightmax;
}
从代码上我们最直观能看出来的是,我们使用process
求解出数组左半部分和右半部分的最大值,然后比较左右两部分得到的最大值的大小,返回该值,作为process
求解该左半部分+右半部分中最大值的结果。这种函数自己调用自己进行求解的方法我们称之为递归。
让我们更加细致的分析这段代码。
递归停止条件是:L == R
,即是该段数据的左右边界重合,也就是说分解这个数组直至其左右两部分为单个元素时。
假设该数组为{5,4,3,7,6,2,9,8,10}
,将其传入该函数process
中,由于此时0=L!=R=9
所以if
中的条件不成立,计算这个数组的中点mid = 4
,将左半部分数据送至process
中求解该部分的最大值。进入到第二个process
计算Leftmax = process(a,0,4)
,此时if
条件还是不成立,继续二分,进入第三个process
计算Leftmax = process(a,0,2)
…直至进入第五个process
计算Leftmax = process(a,0,0)
,if条件成立,return得到Leftmax = process(a,0,0)=a[0]=5
。该过程如下:
得到Leftmax = process(a,0,0)
的返回值后,开始计算Rightmax = process(a,1,1)
此时这个process中的if条件也是成立的,可以计算得到Rightmax = process(a,1,1)=a[1]=4
,然后返回max(Rightmax,Rightmax)
作为process(a,0,1)
的结果也就是process(a,0,2)中的Leftmax = process(a,0,1)=5
,图解如下:
然后按照以上的方式继续进行直至求出 Leftmax = process(a,0,4)
和Rightmax = process(a,5,9)
的值,返回其中的最大值就得到了process(a,0,9)
的结果,也就是我们目标数组的最大值。
也就是说,我们也会发现只有当if条件成立时,此时才不会继续调用process,而是开始返回一个值作为前一层process中的结果,我们也称其为递归停止条件,在该程序中是:L == R
,即是该段数据的左右边界重合,也就是说分解这个数组直至其左右两部分为单个元素时。我们宏观的看一下这个函数的调用过程就是以下:
下图为二分过程:
下图为求解最大值的过程,我们可以将方框看作是求解框内数max的process:
我们将上图与下图对照看(没有带框就是指其为上图对应框中的结果)也就是:
这就是我们二分求解数组最大值的递归过程。
二分法求解数组最大值代码
#include <stdio.h>
int process(int* a,int L,int R){
if(L == R)
return *(a+L);
int mid = L+((R-L)/2);//计算中点
int Leftmax = process(a,L,mid);
int Rightmax = process(a,mid+1,R);
return (Leftmax>Rightmax)?Leftmax:Rightmax;
}
int main(){
int n;
printf("该数组长度为:");
scanf("%d",&n);
int a[n];
printf("请输入一个组数:");
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
int max = process(a,0,n-1);
printf("%d \n",max);
}