算法1:
对于第一种算法比较直接,就是从头到尾计算一遍,然后计算最大值就可以了。时间复杂度为O(n^3).
#include<stdio.h>
int Maxsum(int a[],int n)
{
int sum,maxsum=0;
//i和j分别是子序列的左端和右端
//遍历i,j的所有情况
for(int i=0;i<n;i++)
{
for(int j=i;j<n;j++)
{
sum=0;
//求子序列和
for(int k=i;k<=j;k++)
{
sum+=a[k];
if(sum>maxsum)
maxsum=sum;
}
}
}
return maxsum;
}
int main()
{
int a[10]={4,-3,5,-2,-1,2,6,-2};
printf("%d\n",Maxsum(a,8));
}
算法2:基于第一种方法的改进。算法一中求子序列和是通过从i到j挨个累加的方式,因为求下个子序列和时又得从i开始,造成很多重复,所以算法2中是通过在之前所求子列和的基础上再加1个元素的方式,时间复杂度为O(n^2).
#include<stdio.h>
int Maxsum2(int a[],int n)
{
int sum,maxsum=0;
//i和j分别是子序列的左端和右端
for(int i=0;i<n;i++)
{
sum=0;
for(int j=i;j<n;j++)
{
sum+=a[j];//子序列和为之前子序列加1个数后的值
if(sum>maxsum)
maxsum=sum;
}
}
return maxsum;
}
int main()
{
int a[10]={4,-3,5,-2,-1,2,6,-2};
printf("%d\n",Maxsum2(a,8));
}
算法3:分而治之。
思想是将数列在中间一分为二,则最大子序列和只有3种可能:
1.在左子序列
2.在右子序列
3.跨中间分割线的子序列
通过递归就可以求得最大子序列。时间复杂度为O(nlogn).
#include<stdio.h>
#define max(m,n) (m)>(n)?(m):(n)
int Maxsum3(int a[],int i,int j)
{
int begin=i,end=j,max; //序列的开始和结尾
int mid=(begin+end)/2;
int maxr,maxl,maxn;
int rBorderSum=0,lBorderSum=0;
int maxrBorderSum=0,maxlBorderSum=0;
if(i==j)
{
if(a[i]>0)
return a[i];
else
return 0;
}
else
{
maxl=Maxsum3(a,begin,mid);
maxr=Maxsum3(a,mid+1,end);
for(int k=mid;k>=begin;k--)
{
lBorderSum+=a[k];
if(lBorderSum>maxlBorderSum)
maxlBorderSum=lBorderSum;
}
for(int k=mid+1;k<=end;k++)
{
rBorderSum+=a[k];
if(rBorderSum>maxrBorderSum)
maxrBorderSum=rBorderSum;
}
maxn=maxlBorderSum+maxrBorderSum;
return max(max(maxl,maxr),maxn);
}
}
int main()
{
int a[10]={4,-3,5,-2,-1,2,6,-2};
printf("%d\n",Maxsum3(a,0,7));
}
算法4:在线处理法
也就是边扫描边处理,如果当前扫描子序列和大于原最大子序列和就更新最大子序列和;如果当前子序列和为负数则,舍弃该子序列。
#include<stdio.h>
#define max(m,n) (m)>(n)?(m):(n)
int Maxsum4(int a[],int n)
{
int ThisSum,MaxSum;
ThisSum=MaxSum=0;
for(int i=0;i<n;i++){
//向右累加
ThisSum+=a[i];
if(ThisSum>MaxSum)
MaxSum=ThisSum;//发现更大的就更新当前结果
else if(ThisSum<0)
ThisSum=0;//如果当前子序列和为负数,则抛弃之
}
return MaxSum;
}
int main()
{
int a[10]={4,-3,5,-2,-1,2,6,-2};
printf("%d\n",Maxsum4(a,8));
}