最大连续子序列和及其起始位置(未测试)
/******************************
问题描述:求一个序列中的最大连续子序列和以及起始位置。若全为负数,则返回0。
*******************************/
int start, end;
start = end = 0;
/******************************
算法名称:枚举法
简述:列举所有可能的序列,并求和。前缀相同的序列重复求和,
违背了“同一个数最多计算一次”的原则,导致复杂度上升。
复杂度分析:T(N) = =
时间复杂度:O()
******************************/
int Sum1(const int A[], int n)
{
int i, j, k;
int start, end;
int ThisSum, MaxSum;
MaxSum = 0;
for(i = 0; i < n; i++) /* 确定序列的起始索引 */
for(j = i; j < n; j++){ /* 确定序列的终止索引 */
ThisSum = 0;
for(k = i; k <= j; k++) /* 遍历A[i]~A[j] */
ThisSum += A[k];
if(ThisSum > MaxSum){
MaxSum = ThisSum;
start = i;
end = j;
}
}
return MaxSum;
}
/*****************************
算法名称:枚举法
简述:列举所有可能的序列。但在求和时,避免重复求和,复杂度降低。
算法分析:T(N) = = ()/2
时间复杂度:O()
******************************/
int Sum2(const int A[], int n)
{
int i, j;
int ThisSum, MaxSum;
MaxSum = 0;
for(i = 0; i < n; i++){
ThisSum = 0;
for(j = i; j < n; j++){
ThisSum += A[j]; /* S(j) = A[j] + S(j-1) */
if(ThisSum > MaxSum){
MaxSum = ThisSum;
start = i;
end = j;
}
}
}
return MaxSum;
}
/******************************
算法名称:分治递归法
简述:①每个序列的最大子序列有三个可能的位置:1、序列左半部;2、序列右半部;3、跨越序列左右半部分
1、左半部,用递归求最大子序列和
2、右半部,用递归求最大子序列和
3、从中间元素开始,分别求左半部和右半部最大子序列和,然后求和
②base case:当所求序列只有一个元素时,若为正数,则返回该数,否则返回0。
③返回三个位置中最大的和。
复杂度分析:T(N) = 2T(N/2) + O(N)
时间复杂度:O(NlogN)
*******************************/
int Max3(int a, int b, int c)
{
if(b > a)
a = b;
if(c > a)
a = c;
return a;
}
int SumSub3(const int A[], int left, int right)
{
int center, i;
int MaxLeftSum, MaxRightSum;
int MaxLeftBorderSum, MaxRightBorderSum;
int LeftBorderSum, RightBorderSum;
if(left == right){ /* base case */
if(A[left] > 0)
return A[left];
else
return 0;
}
center = (left + right) / 2;
MaxleftSum = SumSub3(A, left, center); /* 左半部分的最大子序列 */
MaxRightSum = SumSub3(A, center+1, right); /* 右半部分的最大子序列 */
MaxLeftBorderSum = 0; LeftBorderSum = 0; /* 跨左右两部分的最大子序列的左半部分 */
for(i = center; i >= 0; i--){
LeftBorderSum += A[i];
if(LeftBorderSum > MaxLeftBorderSum){
MaxLeftBorderSum = LeftBorderSum;
start = i;
}
}
MaxRightBorderSum = 0; RightBorderSum = 0; /* 跨左右两部分的最大子序列的右半部分 */
for(i = center+1; i <= right; i++){
RightBorderSum += A[i];
if(RightBorderSum > MaxRightBorderSum){
MaxRightBorderSum = RightBorderSum;
end = i;
}
}
return Max3(MaxLeftBorderSum + MaxRightBorderSum, MaxleftSum, MaxRightSum); /* 返回三种子序列中的最大值 */
}
int Sum3(const int A[], int n)
{
return SumSub3(A,0, n-1);
}
/*********************************
算法名称:线性联机算法
简述:重要思想:任何和为负数的子序列都不可能是最大子序列的前缀。
若A[i] 为负数,则A[i] 不可能是最大子序列的前缀,同理,即若A[i] 到A[j]的j-i+1个元素的和为负数,则应将其抛弃,重新求和。
在A[j]之前可能有最大和,会保存在MaxSum中,而ThisSum则重新求和(求和)。
复杂度分析:时间复杂度为O(N),线性。
每次读取一个元素后,不需要存储,直接加到ThisSum上。
而且此算法在任何时刻都能对已读取数据给出当前数据的解。联机算法。
常量空间线性时间的联机算法几乎是完美的算法。
**********************************/
int Sum4(const int A[], int n)
{
int ThisSum, MaxSum;
int i;
ThisSum = MaxSum = 0;
for(i = 0; i < n; i++){
ThisSum += A[i];
if(ThisSum > MaxSum){
MaxSum = ThisSum;
end = i;
}
else if(ThisSum < 0) /* 当前子序列的和为负,则应重新求和 */
ThisSum = 0;
start = i+1;
}
return MaxSum;
}