最大子序列和问题的四种解法以及解析

问题描述

求取数组中最大连续子序列和,例如给定数组为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;
}

 

你可能感兴趣的:(基础算法,最大子序列和问题,算法)