求数组的连续子数组之和的最大值
输入一个N个元素的整型数组,数组里有正数也有负数。数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。求所有子数组的和的最大值。
例如输入的数组为-9 -3 -2 2 -1 2 5 -7 1 5,和最大的子数组为2 -1 2 5。因此输出为该子数组的和8。
可是如果都是负数的话,要返回0?还是返回最小的负数?,这个数时候你要问问面试官(交流很重要)。
OK,想一想该如何做?遍历?遍历是最伟大的方法,几乎多有的题目都可解答,堪称万能解题法。
好吧,咱就先用遍历来解决一下。Sum[i-j]代表i-j元素的和,需要N次遍历,每一次遍历都要求Sum[i-j]。
编码开始
int MaxSum(int array[],int start,int end)
{
int MaxSum=-INFINITY;//最大值初始化为极小值
int sum=0;
for(int i=start;i<=end;i++)
{
sum=0; //记得每次初始化,我们求的是子数组的和
for(int j=i;j<=end;j++)
{
sum+=array[j];
if(sum>MaxSum)
MaxSum=sum;
}
}
return MaxSum;
}
时间复杂度是O(n^2)。还凑合吧,还能继续优化吗?
想一想,之前学过的方法。分治法,分而治之,可以吗?YES
如果我们把数组array[1-N]分成两部分array[0-N/2-1]和array[N/2-N-1],那么我们要求的连续子数组最大和就有了3种可能:
1 原始数组最大和就是array[0-N/2-1]数组的最大和
2 原始数组最大和就是array[N/2-N-1]数组的最大和
3 最大和的子数组是包括array[N/2-1]和array[N/2]的一段子数组。
前两种情况可以用递归解决,第三种情况从Mid向两边遍历一次O(n)就可以了。
递归要递归到只有一个元素的时候直接返回即可。时间复杂度很明显是O(nlgn)。
编码实现:
int MaxSum(int array[],int start,int end)
{
int LeftSum,RightSum,MidLeftSum,MidRightSum;
int max,sum;
int i;
if(end==start)
return array[start];
int pivot=start+(end-start)/2;
LeftSum=MaxSum(array,start,pivot);
RightSum=MaxSum(array,pivot+1,end);
max=-INFINITY; //最大值初始化为极小值
sum=0;
for(i=(end-start+1)/2-1;i>=start;i--) //求以array[N/2-1]结尾的一段最大和
{
sum+=array[i];
if(sum>max)
max=sum;
}
MidLeftSum=max;
max=-INFINITY;
sum=0;
for(i=(end-start+1)/2;i<=end;i++) //求以array[N/2]开始的一段最大和
{
sum+=array[i];
if(sum>max)
max=sum;
}
MidRightSum=max;
return Max(LeftSum,MidLeftSum+MidRightSum,RightSum);
}
时间复杂度降到了O(nlgn),还不错。可是我们不能仅仅满足于这样的复杂度,O(n)才是我们追求的目标。可是这个题目可以达到吗?
想一想,这个题目有什么性质?
最优子结构?</