寻找最大子列元素和的巧妙算法
根据用户的输入生成一个数列A,要求出:
∑
k
=
i
j
A
k
\sum_{k=i}^jA_k
k=i∑jAk
的最大值。
为了方便表述,我们记满足要求的数列为B,假设B中共有m个元素,并且
B
0
=
A
i
,
B
m
−
1
=
A
j
B_0=A_i, B_{m-1}=A_j
B0=Ai,Bm−1=Aj
也就是说
A
i
是
B
中
的
第
一
个
元
素
,
A
j
是
B
中
的
最
后
一
个
元
素
A_i是B中的第一个元素,A_j是B中的最后一个元素
Ai是B中的第一个元素,Aj是B中的最后一个元素
int MaxSubSum(int N) //N是用户输入数列元素个数-1
{
int i, TheSum, MaxSum;
TheSum = MaxSum = 0;
for (i = 0; i < N; i++)
{
TheSum += NumberList[i]; //1
if (TheSum > MaxSum)
{
MaxSum = TheSum; //2
}
else if (TheSum < 0)
{
TheSum = 0; //3
}
}
}
为什么这段代码能够成功找到目标值呢?我们可以通过下面这个例子来理解:
用户输入:
4 -3 5 -8 7 6 2 -1
我们重点分析一下 i=3 的时候函数中发生了什么。
首先执行语句1,TheSum 由6变成了-2,接着执行语句3,将 TheSum 置零。
为什么要这么做呢,理由很简单:
B中不必可能含有-8(原因将稍后解释)
换言之,-8这个元素将整个数列分成了两部分,其中一部分是-8左侧的子列,记为C
4 -3 5
另一部分是-8右侧的子列,记为D
7 6 2 -1
我们所求的B就只能是C、D以及它们的子列中的一个。显然,在这道题里,D就是我们要找的B。
而为什么B中一定没有-8呢?
不妨让我们这样理解寻找目标子列的过程:
4 -3 5 -8 7 6 2 -1
该数列等价于
1 5 -8 7 6 2 -1(4和-3加在了一起)
进而
6 -8 7 6 2 -1
进而
-2 7 6 2 -1(数列的最左侧是负数)
截止目前, MaxSum 中储存的数据是6
在我们寻找目标子列时,我们有时会选择性的把负数加到我们的子列当中,因为我们期待着在负数后面我们可以遇到一个绝对值更大的正数,这样就能让子列最终的元素和变大。但是有一种情况下负数是一定不能加入到子列中的——那就是负数在数列的最左侧的情况。而-8的出现就造成了这种情况,因此我们只得以-8为间断点将数列分成两部分再来寻找目标子列。
把-2抛弃后
7 6 2 -1
进而
13 2 -1(由于 13>6 ,MaxSum 的数据更新)
进而
15 -1
进而
14
此时 MaxSum=14