最近初学编程学习了一个很经典的入门算法题——最大连续子列和问题,为了加深自己的印象,总结了各种解法。
题目:给定一个非空序列,其中可以包括自然数和负数,找出它连续的一个子序列,使子序列的和最大
例:10
5 7 -6 -8 8 9 5 2 -7 3
其中,10代表序列长度,该序列的最大连续子序列和是22,即第一个数字5累和到第八个数字2。
解法一:暴力法(穷举法) 复杂度O(N^3)
该种解法非常简单直接暴力血腥,我们只需要用两层循环分别枚举序列起点和终点,这样就能得到所有连续子序列,期间不断更新最大子序列的和即可。
public static int MaxSubseqSum1(int a[], int n) {
int ThisSum, MaxSum = 0;
for (int i = 0; i < n; i++)// i是子列左端的位置
{
for (int j = i; j < n; j++)// j是子列右端的位置
{
ThisSum = 0;// ThisSum是a[i]到a[j]的子列和
for (int k = i; k <= j; k++) {
ThisSum += a[k];
if (ThisSum > MaxSum)//如果得到的这个子列和更大
MaxSum = ThisSum;//则更新结果
}
}
}
return MaxSum;
}
解法二:暴力法优化 复杂度O(N^2)
分析解法一,发现该算法做了无用功,当j增加1时,没必要从头算起,只需要在i和j的循环之间定义ThisSum=0计算a[i]到a[j]的子列和即可。
public static int MaxSubseqSum2(int a[],int n) {
int ThisSum,MaxSum=0;
for(int i=0;i<n;i++) {
ThisSum=0;
for(int j=i;j<n;j++) {
ThisSum+=a[j];
if(ThisSum>MaxSum)
MaxSum=ThisSum;
}
}
return MaxSum;
}
解法三:分治法 复杂度O(NlgN)
解法四:在线处理法 复杂度O(N)
在线处理法,顾名思义就是输入一个数就处理一个数,其大概思想就是一旦前面子序列的和出现了负数,就直接舍去,保证最大子列和递增,代码真的贼鸡儿简单。
例:10
5 7 -6 -8 8 9 5 2 -7 3
从第一个元素(5)加到第四个元素(-8)时发现序列和小于0,继续加只会使后面的和加上一个负数而更小,所以应该舍去前四个序列和,再从第五个元素开始,思想就是这个。
public static int MaxSubseqSum4(int a[],int n) {
int thisSum=0,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;
}