给出一个序列,求出最大子列和。
算法一:
long MaxSum(int A[],int N)
{
int i,j,k;
int ThisSum=0,MaxSum=0;
for(i=0;i<N;i++) //i为子序列左端
{
for(j=i;j<N;j++) //j为子序列右端
{
ThisSum0=0;
for(k=i;k<=j;k++)
ThisSum += A[k]; //A[k]将子序列从i加到j
if(ThisSum>MaxSum)
MaxSum=ThisSum;
}
}
return MaxSum;
}
这是一个穷举的算法,有三个嵌套的for循环,时间复杂度为O(n^3) 。在计算的时候有很多不必要的重复项,例如当i=0,j=3时,和的计算为:A[1]+A[2]+A[3];当i=0,j=4时,和为A[1]+A[2]+A[3]+A[4],A[1]+A[2]+A[3]的部分重合了。改进的措施是:保留之前的和,在之前和的基础上加入新的值。
算法二:
long MaxSum(int A[],int N)
{
int i,j;
int ThisSum=0,MaxSum=0;
for(i=0;i<N;i++)
{
ThisSum=0;
for(j=0;j<N;j++)
{
ThisSum +=A[j];
if(ThisSum>MaxSum)
MaxSum=ThisSum;
}
}
return MaxSum;
}
这个算法的时间复杂度为O(n^2),这个算法在第一个for循环的开始就将ThisSum置0,之后每加上一个数的和就与MaxSum相比较,大于就将ThisSum赋给MaxSum。
算法三
long MaxSum(int A[],int left,int right)
{
if(left==right) //如果左右下标相等,且左下标对应的值大于0则返回
{
if(A[left]>0)
return A[left];
else
return 0;
}
int center=(left+right)/2; //求出中间下标值
long MaxLeftSum=MaxSum(A,left,center); //左子序列最大值
long MaxRightSum=MaxSum(A,center+1,right); //右子序列最大值
long MaxLeftBoarderSum=0,LeftBoarderSum=0;
for(i=center;i>=left;i--) //从中间加确保左部分的最右一个值能被加上
{
LeftBoarderSum += A[i];
if(LeftBoarderSum>MaxLeftBoarderSum)
MaxLeftBoarderSum=LeftBoarderSum;
}
long MaxRightBoarderSum=0,RightBoarderSum=0;
for(i=center+1;i<=right;i--)
{
RightBoarderSum += A[i];
if(RightBoarderSum>MaxRightBoarderSum)
MaxRightBoarderSum=RightBoarderSum;
}
return max3(MaxLeftSum,MaxRightSum,
MaxLeftBoarderSum+MaxRightBoarderSum)); //返回三者最大值
}
long max3(long a,long b,long c)
{
if(a<b)
a=b;
if(a<c)
return c;
else
return a;
}
算法三的时间复杂度为:O(NlogN) ,算法三采用“分治策略”:把数组分成左右两个部分,最大子序列可能的出现的位置为左半部分,右半部分,横跨左右的中间部分。左右两个部分可以通过递归调用求出最大子序列和,中间部分需要将左半部分最大子序列和加右半部分子序列和,其中,左半部分最大子序列和要将左半部分的最右一个值求上,右半部分子序列和要将最左一个值求上。
算法四
long MaxSum(int A[],int N)
{
long MaxSum = 0, ThisSum = 0;
for (int j = 0; j <N; j++)
{
ThisSum += A[j];
if (ThisSum > MaxSum)
MaxSum = ThisSum;
else if (ThisSum < 0)
ThisSum = 0;
}
}
算法四的时间复杂度为O(N) ,思想是:加起来的和如果小于-1即把子序列和归零,因为小于零的数不会让后面加进来的数变大。