参考:数据结构与算法分析——Java语言描述
(美) Mark Allen Weiss
给定整数 A1,A2,……AN (可能有负数),求这个整数序列的最大子序列和。( 原书假定如果所有整数为负数,则最大的子序列的和为0。我们这里去掉了这个假定,算法1和算法2只需要把maxSum初始设为 a[0] 即可,算法3做了些修改)例如:-2 ,11,-4,13,-5,-2, 答案为 20(从A2--A4)
算法1 :
算法1是一种比较容易想到的方法。我们可以这样想,这个子序列可能从第1个元素开始,也有可能从第2、第3、……个元素开始。我们初始假设最大的子序列和 maxSum 是第一个元素。然后分别从第1、第2、………个元素开始计算子序列和,并和当前的和 maxSum 比较,如果大于 maxSum,就将此子序列和赋值给maxSum。
代码如下:
//算法1
public int maxSubSum1(int []a){
int thisSum=0,maxSum=a[0];
for(int i=0;i<a.length;i++)
{
thisSum=0;
for(int j=i;j<a.length;j++)
{
thisSum+=a[j];
if(thisSum>maxSum){
maxSum=thisSum;
}
}
}
return maxSum;
}
算法2:
如图:
我们可以把这个整数数列分成前后两部分。那么最大的子序列和可能出现在三处:前半部分某子序列(设其和为maxLeft),后半部分某子序列(设其和为maxRight),中间部分某子序列(设其和为maxCenter)。前两种情况可以通过递归求解。第三种情况,我们通过分析可知,这种情况下的最大和可以通过求出前半部分的最大和(包含前半部分的最后一个元素)以及后半部分的最大和(包含后半部分的第一个元素)而得到。
代码如下:
//算法2
public int maxSubSum2(int []a,int left,int right){
int maxSum=a[0];
if(left==right){
if(a[left]>maxSum)
maxSum=a[left];
}else{
int center=(left+right)/2;
//左半部分最大子序列和
int maxLeft=maxSubSum2(a,left, center);
//右半部分最大子序列和
int maxRight=maxSubSum2(a, center+1, right);
//求左半部分包含最后一个元素的最大和
int maxLeftBorder=a[center],leftBorderSum=0;
for(int i=center;i>=left;i--){
leftBorderSum+=a[i];
if(leftBorderSum>maxLeftBorder){
maxLeftBorder=leftBorderSum;
}
}
//求右半部分包含第一个元素的最大和
int maxRightBorder=a[center+1],rightBorderSum=0;
for(int j=center+1;j<=right;j++){
rightBorderSum+=a[j];
if(rightBorderSum>maxRightBorder){
maxRightBorder=rightBorderSum;
}
}
//横跨前后两部分的最大子序列和
int maxCenter=maxLeftBorder+maxRightBorder;
maxSum=Math.max(maxCenter,Math.max(maxLeft, maxRight));
}
return maxSum;
}
算法3:
这种算法效率最高。thisSum每加一个元素后,判断它是否大于 maxSum ,如果大于则 maxSum=thisSum 。判断 thisSum是否小于0,如果小于0,那么说明计算到当前这个位置上的子序列的和是个负数。thisSum=0的效果就相当于把子序列的起始位置推进到当前这个子序列的最后一个位置+1,开始一个新的子序列了。
代码如下:
//算法3
public int maxSubSum3(int []a){
int maxSum=a[0],thisSum=0;
for(int i=0;i<a.length;i++){
thisSum+=a[i];
if(thisSum>maxSum){
maxSum=thisSum;
}
if(thisSum<0){
thisSum=0;
}
}
return maxSum;
}