主要是课堂PPT上的内容,但是觉得很有启发意义,遂记录下来
题目
给定(可能是负的)整数序列A1,A2。。。An,寻找(并标识)值为最大的序列。如果所有的整数都是负的,那么最大连续子序列的和是零。
例如,假设输入是{-2, 11, -4, 13, -5, 2},那么答案是20,它表示连续子序列包含了第2项到第4项(如粗体字部分)。又如第二个例子,对于输入{1, -3, 4, -2, -1, 6},答案是7,这个子序列包含最后四项。
解一:穷举法
思路:
分别求子序列
[0,0],[0,1],。。。,[0,n]
[1,1],[1,2],。。。[1,n]
。。。
[n,n]
的和,求出最大值
代码
int maxSubsequenceSum(int a[], int size, int &start, int &end)
{ int maxSum = 0;
for (int i = 0; i < size; i++ )
for( int j = i; j < size; j++ )
{ int thisSum = 0;
for( int k = i; k <= j; k++ )
thisSum += a[ k ];
if( thisSum > maxSum )
{ maxSum = thisSum;
start = i; end = j;
}
}
return maxSum;
}
算法分析
最里层的语句thisSum += a[ k ];在最里层循环中执行j-i+1次。第二个循环的规模是n-i,最外层的循坏规模是n。因此最里层语句执行的次数是
即O(n3)
解二:穷举法的优化
思路
最内层循环已经包含在第二层循环内,所以可以省略
代码
int maxSubsequenceSum(int a[], int size, int &start, int &end)
{ int maxSum = 0;
for( int i = 0; i < size; i++ ){
int thisSum = 0;
for( int j = i; j < size; j++ ){
thisSum += a[j];
if( thisSum > maxSum ){
maxSum = thisSum;
start = i; end = j;
}
}
}
return maxSum;
}
算法分析
少了内层循环的n次遍历,所以时间复杂度为O(n2)
解三:O(n)规模的遍历
思路
如果一个子序列的和是负的,则它不可能是最大连续子序列的一部分,因为我们可以通过不包含它来得到一个更大的连续子序列。如:{-2, 11, -4, 13, -5, 2}的最大子序列不可能从-2开始;{1, -3, 4, -2, -1, 6}的最大子序列不可能包含{1,-3}
所有与最大连续子序列毗邻的连续子序列一定有负的(或0)和(否则会包含它们)。
在算法二中,当检测出一个负的子序列时,不但可以从内层循环中跳出来,而且还可以让i直接增加到j+1。如:{1, -3, 4, -2, -1, 6},当检测序列[1,-3]后,发现是负值,则表示该子序列不可能包含在最大子序列中。因此,接下去就可以从4开始检测。
注意:当检测序列[1,-3]后,可以确定他不在最大子序列中,但1仍可能是最大子序列,如果-3后面都是负值。因此在找到新的最大子序列前,必须保存1是最大子序列的事实。
代码
int maxSubsequenceSum(int a[], int size, int &start, int &end){
int maxSum = 0, thisSum = 0, startTmp = 0;
start = end = 0;
for( int j = 0; j < size ; j++ ){
thisSum += a[j];
if ( thisSum < 0 ) thisSum = 0;
else if (thisSum == a[j]) //可能是新的最大子序列
startTmp = j;
if( thisSum > maxSum ){
maxSum = thisSum; start = startTmp; end = j;
}
}
return maxSum;
}
算法分析
时间复杂度为O(n)