子列和列_最大子列和 - fanlinglong - 博客园

最大连续子序列和问题

给定k个整数的序列{N1,N2,...,Nk },其任意连续子序列可表示为{ Ni, Ni+1, ..., Nj },其中 1 <= i <= j <= k。最大连续子序列是所有连续子序中元素和最大的一个,例如给定序列{ -2, 11, -4, 13, -5, -2 },其最大连续子序列为{11,-4,13},最大连续子序列和即为20。

注:为方便起见,如果所有整数均为负数,则最大子序列和为0。

解决这样一个问题是一个很有趣的过程,我们可以尝试着从复杂度比较高的算法一步一步地推出复杂度较低的算法。

算法一:

时间复杂度:O(N^3)

其代码:

int MaxSubSequence(const int A[], intN){intThisSum,MaxSum,i,j,k;

MaxSum= 0;for(i=0;i

{for(j=i;j

{

ThisSum= 0;for(k=i;k<=j;k++)

{

ThisSum+=A[k];

}if(ThisSum >MaxSum)

MaxSum=ThisSum;

}

}returnMaxSum;

}

对于此种算法,其主要方法是穷举法,即求出该序列所有子序列的序列和,然后取最大值即可。

算法二:

时间复杂度:O(N^2)

其代码:

int MaxSubSequence(const int A[], intN){intThisSum,MaxSum,i,j;

MaxSum= 0;for(i=0;i

{

ThisSum= 0;for(j=i;j

{

ThisSum+=A[j];if(ThisSum >MaxSum)

MaxSum=ThisSum;

}

}returnMaxSum;

}

对于这种方法,归根究底还是属于穷举法,其间接地求出了所有的连续子序列的和,然后取最大值即可。

那么,这里,我们需要对比一下前面两种算法,为什么同样都是穷举法,但算法一的时间复杂度远高于算法二的时间复杂度?

算法二相较于算法一,其优化主要体现在减少了很多重复的操作。

对于A-B-C-D这样一个序列,

算法一在计算连续子序列和的时候,其过程为:

A-B、A-C、A-D、B-C、B-D、C-D

而对于算法二,其过程为:

A-B、A-C、A-D、B-C、B-D、C-D

其过程貌似是一样的,但是算法一的复杂就在于没有充分利用前面已经求出的子序列和的值。

举个例子,算法一在求A-D连续子序列和的值时,其过程为A-D = A-B + B-C + C-D;

而对于算法二,A-D连续子序列和的求值过程为A-D = A-C+C-D;

这样,算法二充分利用了前面的计算值,这样就大大减少了计算子序列和的步骤。

算法三:递归法(分治法)

时间复杂度:O(NlogN)

易知,对于一数字序列,其最大连续子序列和对应的子序列可能出现在三个地方。或是整个出现在输入数据的前半部(左),或是整个出现在输入数据的后半部(右),或是跨越输入数据的中部从而占据左右两半部分。前两种情况可以通过递归求解,第三种情况可以通过求出前半部分的最大和(包含前半部分的最后一个元素)以及后半部分的最大和(包含后半部分的第一个元素)而得到,然后将这两个和加在一起即可。

其实现代码为:

int MaxSubSequence(const int A[],intN)

{return MaxSubSum(A,0,N-1);

}static int MaxSubSum(const int A[], int Left, intRight)

{intMaxLeftSum,MaxRightSum;intMaxLeftBorderSum,MaxRightBorderSum;intLeftBorderSum,RightBorderSum;intCenter,i;if(Left ==Right)

{if(A[Left] > 0)returnA[Left];else

return 0;

}

Center= (Left + Right)/2;

MaxLeftSum=MaxSubSequence(A,Left,Center);

MaxRightSum= MaxSubSequence(A,Center+1,Right);

MaxLeftBorderSum= 0;

LeftBorderSum= 0;for(i = Center;i >= Left;i--)

{

LeftBorderSum+=A[i];if(LeftBorderSum >MaxLeftBorderSum)

MaxLeftBorderSum=LeftBorderSum;

}

MaxRightBorderSum= 0;

RightBorderSum= 0;for(i = Center+1;i <= Right;i++)

{

RightBorderSum+=A[i];if(RightBorderSum >MaxRightBorderSum)

MaxRightBorderSum=RightBorderSum;

}return Max(MaxLeftSum,MaxRightSum,MaxLeftBorderSum +MaxRightBorderSum);

}int Max(int a, int b, intc)

{if(a>b&&a>c)returna;else if(b>a&&b>c)returnb;else

returnc;

}

现在对上面的代码进行相关说明:

Center变量所确定的值将处理序列分割为两部分,一部分为Center前半部,一部分为Center+1后半部。

在上文,我们提到,最大连续子序列的出现位置有三种情况。

对于前两种情况,我们根据递归特性,可以得到:

MaxLeftSum =MaxSubSequence(A,Left,Center);

MaxRightSum= MaxSubSequence(A,Center+1,Right);

而对于第三种情况,我们需要先求出前半部包含最后一个元素的最大子序列:

MaxLeftBorderSum = 0;

LeftBorderSum= 0;for(i = Center;i >= Left;i--)

{

LeftBorderSum+=A[i];if(LeftBorderSum >MaxLeftBorderSum)

MaxLeftBorderSum=LeftBorderSum;

}

然后,再求出后半部包含第一个元素的最大子序列:

MaxRightBorderSum = 0;

RightBorderSum= 0;for(i = Center+1;i <= Right;i++)

{

RightBorderSum+=A[i];if(RightBorderSum >MaxRightBorderSum)

MaxRightBorderSum=RightBorderSum;

}

最后,我们只需比较这三种情况所求出的最大连续子序列和,取最大的一个,即可得到需要求解的答案。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值