(1)暴力枚举求解
时间复杂度O(n^3)
int MaxSubseqSuml(int List[],int N){//在长度为N的数组List中求最大子列和的值
int i,j,k;//定义三个变量以便循环
int ThisSum,MaxSum=0;//定义两个变量分别记录当前子列和与当前最大子列和
for(i=0;i<N;i++)//i,j双重循环,求从i到j的子列和
for(j=1;j<N;j++){
ThisSum=0;//初始化当前子列和(新的j就需要置空)
for(k=i;k<=j;k++)//i到j求和
ThisSum+=List[k];
if(ThisSum>MaxSum)//如果子列和大于当前最大子列和
MaxSum=ThisSum;//则更新当前最大子列和
}
return MaxSum;//返回最大子列和的值
}
(2)改进避重求解
时间复杂度O(n^2)
int MaxSubseqSuml(int List[],int N){//在长度为N的数组List中求最大子列和的值
int i,j;//定义两个变量以便循环
int ThisSum,MaxSum=0;//定义两个变量分别记录当前子列和与当前最大子列和
for(i=0;i<N;i++){//i为起点的子序列
ThisSum=0; //重置当前子列和的值
for(j=i;j<N;j++){//从从起点开始,终点(下标)可以是i到N-1
ThisSum+=List[j]; //当i==j时,ThisSum=List[i],其之后有i到j+1的和为i到j的和加List[j+1]
if(ThisSum>MaxSum) //一重循环计算了以i为起点的所有子列和,ThisSum记录上一次的子列和,加List[j]即上一步之后为i到j即新求得子列和
MaxSum=ThisSum; //每一个与最大子列和比较,若大于最大子列和,则更新
}
}
return MaxSum;//返回最大子列和的值
}
(3)分治法求解
时间复杂度为O(N*log2 N)
分治法求解最大子列和就是 将原序列的最大子列和问题分解。思考最大子列可能在哪里,那么在哪里?可能在原序列左端点到中间之间,也可能是原序列中间到右端点之间,还有可能原序列经过中间点,即必定包含中间点往左端点有一段,往右端点右一段。所以我们分别求这三类情况的最大子序列,再求出最大值,就是全集(原问题)的最大值。对于左右端点到中间序列求最大子列和,与原问题属于同一类直接递归求解即可。对于经过中间点的序列,我们已经有中间点需要往两边延申试探最大和序列起点和终点,延申过程分解为从中间点向左延申和向右延申,分别求最大序列和(即从中间点分别向左向右出发,分别找起点和终点)最后两段最大合并就是经过中间点的最大子序列。
int Max3(int a,int b,int c)//返回a b c中的最大值
{
return a>b? a>c?a:c : b>c?b:c;
//重点是要知道a>b?无论是否成立,执行的是一条语句
//原语句等价于 a>b? (a>c?a:c) :(b>c?b:c)
//每个括号里的问号表达式里才是一条语句。所有情况如下所示
//a>b a>c a
//a>b a<=c c
//a<b b>c b
//a<b b<=c c
}
int DivideAndConquer(int List[],int left,int right)//求解List序列从left到right的最大子列和
{
//定义两个变量分别记录左端到中间的序列的最大子列和 和中间到右端的最大子列和
int MaxLeftSum,MaxRightSum;
int MaxLeftBorderSum,MaxRightBorderSum;
//存放从左端点到中间点的连续(不中断)的序列的子列和的最大值 和从中间点到右端点(不中断)的序列的子列和最大值
int LeftBorderSum,RightBorderSum;//记录求左端点到中间点的连续序列子列和的值(中间存储变量),Right同理...
int center,i;//分别用作计算中间节点下标和循环
if(left==right){//如果序列中只有一个元素
if(List[left]>0)return List[left];//如果此元素为正则返回其(它就是最大子列和)
else return 0;//根据书本,我们规定如果最大子列和为负值则以0为最大子列和的值
}
center=(left+right)/2;//计算中间节点下标
MaxLeftSum=DivideAndConquer(List,left,center);//递归求解左序列中子列最大和
MaxRightSum=DivideAndConquer(List,center+1,right);//递归求解右序列中子列最大和
MaxLeftBorderSum=0,LeftBorderSum=0;//将需要使用的初始化为零
for(i=center;i>=left;i--){//求解跨越中间点向左(不中断)的最大子列和
LeftBorderSum+=List[i];//因为不中断所以每个值都要加,相当于不断寻找子列的起点
if(LeftBorderSum>MaxLeftBorderSum)//需要记录最大值,如果找到更大的则更新
MaxLeftBorderSum=LeftBorderSum;
}
MaxRightBorderSum=0,RightBorderSum=0;
for(i=center+1;i<=right;i++){//同理求解跨越中间点+1向右(不中断)的最大子列和
RightBorderSum+=List[i];
if(RightBorderSum>MaxRightBorderSum)
MaxRightBorderSum=RightBorderSum;
}
return Max3(MaxLeftSum,MaxRightSum,MaxLeftBorderSum+MaxRightBorderSum);
//三种情况中的最大值就是最大子列和的值(跨越中间的最大子列和的值等于左边最大值加右边最大值)
}
int MaxSubseqSuml(int List[],int N){//在长度为N的数组List中求最大子列和的值(封装函数,为了保持接口(参数列表)的一致)
return DivideAndConquer(List,0,N-1);//返回求得的最大子列和的值
}
(4)在线处理
在线处理的逻辑依据是 假设有一段i到j为序列的最大子序列,则任意x>=i&&x<=j(x在最大子序列中),都一定有Sum(i,x)>=0,**即最大子序列的任意从起点出发的内部序列都应该大于等于0,否则如果小于零,那么舍去这段岂不是序列和会变大!**所以基于此开始遍历序列,遇到一个数就加入当前序列,如果序列和大于最大值则更新,如果序列和小于0则舍去序列(即将序列和清空,从下一个重新加)。这样它一定会加到最大序列(在最大序列前的序列肯定和为负被清空),然后从最大序列头开始记录序列,直到超出最大序列(没有关系,序列值已被记录,最大值被更新),于是就求出最大子序列和。
int MaxSubseqSuml(int List[],int N){//在长度为N的数组List中求最大子列和的值
int i;//便于循环
int ThisSum=0,MaxSum=0;//分别记录当前序列和值和最大和
for(int i=0;i<N;i++){//检索整个序列
ThisSum+=List[i];//遇到一个数就加入序列
if(ThisSum>MaxSum){//如果当前序列和大于最大和
MaxSum=ThisSum;//则更新最大和
}
else if(ThisSum<0){//如果当前和小于零(else为了少检查一遍if,因为MaxSum至少是0,大于它就不为负)
ThisSum=0;//如果序列为负则丢掉重新寻找序列
}
}
return MaxSum;//返回最大和
}