求取数组中最大连续子序列和,例如给定数组为A={1, 3, -2, 4, -5}, 则最大连续子序列和为6,即1+3+(-2)+ 4 = 6。
(一)穷举法no.1
穷举式的尝试所有可能,这里就不多做解释,这里的算法复杂度易得为O(N^3).
#include
int MaxSubSequenceSum(const int A[], int N){
int ThisSum, MaxSum, i, j,k;
MaxSum = 0;
for(i=0;i < N; i++){
for(j=i;j < N;j++){
ThisSum = 0;
for(k=i;k <= j;k++){
ThisSum += A[k];
if(ThisSum > MaxSum){
MaxSum = ThisSum;
}
}
}
}
return MaxSum;
}
int main(){
int number[6];
int result;
for(int i=0;i<6;i++){
scanf("%d", &number[i]); //在这里%d后不可以添加\n,因为这会造成编译器会在读取一次\n;
}
result = MaxSubSequenceSum(number, 6);
printf("result = %d\n", result);
return 0;
}
(二)穷举法no.2
分析上面的算法可知,在上述情况下会产生大量重复的运算,譬如在第三层循环中,该算法过分耗时了,所以我们可以通过撤销一个for循环,来降低上述的算法的复杂度。因为消除了一个for循环,所以也易得上述算法复杂度为O(N^2)。
#include
int MaxSubSequenceSum(const int A[], int N){
int ThisSum, MaxSum, i, j,k;
MaxSum = 0;
for(i=0;i < N; i++){
ThisSum = 0;
for(j=i;j < N;j++){
ThisSum += A[j];
if(ThisSum > MaxSum){
MaxSum = ThisSum;
}
}
}
return MaxSum;
}
int main(){
int number[6];
int result;
for(int i=0;i<6;i++){
scanf("%d", &number[i]); //在这里%d后不可以添加\n,因为这会造成编译器会在读取一次\n;
}
result = MaxSubSequenceSum(number, 6);
printf("result = %d\n", result);
return 0;
}
(三)递归以及分治法
让我们按照这样的思路进行思考?。在上述问题中,最大子序列的和只可能在三处地方出现(需要先将所有输入分为两个部分,左半部分亦或右半部分)。即或者整个出现在输入数据的左半部分,或整个出现在输入数据的右半部分,或者整个跨越输入数据的中部从而占据左右两半部分。前两中情况我们可以采用递归求解的方式,而第三种情况的最大和可以通过求出前半部分的最大和(包含前半部分的最后一个元素)以及后半部分的最大和(包含后半部分的最后一个元素)而得到。然后将这两个和加在一起,即为第三中情况之和。易知,分治法的复杂度为O(logn),又因为for循环最多只有一层,虽然存在两次,所以该算法的复杂度为O(NlogN)。
#include
static int MaxSubSum(const int A[], int Left, int Right){
int MaxLeftSum, MaxRightSum;
int MaxLeftBorderSum, MaxRightBorderSum;
int LeftBorderSum, RightBorderSum;
int Center, i;
if(Left == Right){
if (A[Left]>0)
{
return A[Left];
}
else{
return 0;
}
}
Center = (Right + Left)/2;
MaxLeftSum = MaxSubSum(A, Left, Center);
MaxRightSum = MaxSubSum(A, Center+1, Right);
MaxLeftBorderSum = 0; MaxRightBorderSum = 0;
for (i = Center; i >= Left; i--)
{
LeftBorderSum += A[i];
if (LeftBorderSum > MaxLeftBorderSum)
{
MaxLeftBorderSum = LeftBorderSum;
}
}
for (i = Center+1; i <= Right; i++)
{
RightBorderSum += A[i];
if (RightBorderSum > MaxRightBorderSum)
{
MaxRightBorderSum = RightBorderSum;
}
}
return Max3(MaxLeftSum, MaxRightSum, MaxRightBorderSum + MaxLeftBorderSum);
}
int MaxSubSequenceSum(const int A[ ], int N){
return MaxSubSum(A, 0, N-1);
}
int main(){
int number[6];
int result;
for(int i=0;i<6;i++){
scanf("%d", &number[i]); //在这里%d后不可以添加\n,因为这会造成编译器会在读取一次\n;
}
result = MaxSubSequenceSum(number, 6);
printf("result = %d\n", result);
return 0;
}
(四)联机算法
该算法只对数据进行一次扫描,一旦A[ i ]被读入并被处理,它就不再需要被记忆。分析该算法容易知道,因为该问题不需要最大序列和的坐标输出,所以我们不需要记忆序列位置,仅仅记忆当前最大序列和的值即可。并且该问题又是线性相加,不存在错位相加,所以使用该算法就可以在复杂度为O(N)下得到,下面看代码。
#include
int MaxSubSequenceSum(const int A[ ], int N){
int ThisSum, MaxSum, j;
ThisSum = MaxSum = 0;
for (j= 0; j < N; j++)
{
ThisSum += A[j];
if (ThisSum > MaxSum)
{
MaxSum = ThisSum;
}
else if(ThisSum < 0){
ThisSum = 0;
}
}
return MaxSum;
}
int main(){
int number[6]; //这里自己设置
int result;
for(int i=0;i<6;i++){
scanf("%d", &number[i]); //在这里%d后不可以添加\n,因为这会造成编译器会在读取一次\n;
}
result = MaxSubSequenceSum(number, 6);
printf("result = %d\n", result);
return 0;
}