一、问题提出
给定一个序列,其中可能有正数也可能有负数,找出其中连续的一个子数列(不允许空序列),使它们的和尽可能大
二、算法分析
对于任意一个序列,可以发现最大子序列和只有三种情况:
1. 出现在数组的左半部分
2. 出现在数组的右半部分
3. 出现在数组的中间部分,横跨左右两部分
而且对于左半部分或者右半部分,上述结论也成立,利用这个特性,可以写出相应的递归,递归结束的条件是当只有一个元素时,判断这个元素是否大于零,大于零便返回这个数,否则返回零。
然后求出左边最大值,右边最大值和横跨两边的最大值,返回这三个值中的最大值
三、程序设计
#include <stdio.h>
#include <stdlib.h>
//求三个数中的最大值
int maxNum(int, int, int);
//递归求最大子序列和,参数列表为:数组名、起始索引,结束索引
int maxSubArraySum(int [], int, int);
int main(){
int *A,n;
printf("请输入序列A的长度(大小),按回车结束: \n");
scanf("%d",&n);
A = (int*)malloc(sizeof(int)*n);
printf("请输入序列A的元素,以空格隔开,按回车结束: \n");
for(int i = 0; i < n; i++){
scanf("%d",&A[i]);
}
printf("输入的序列A为:\n");
for(int i = 0; i < n; i++){
printf(" %d\t",A[i]);
}
printf("\n最大子序列和为: %d\n", maxSubArraySum(A,0,n-1));
return 0;
}
//求三个数中的最大值
int maxNum(int a, int b, int c){
if(a > b)
return a > c ? a : c;
else
return b > c ? b : c;
}
//递归求最大子序列和,参数列表为:数组名、起始索引,结束索引
int maxSubArraySum(int A[], int left, int right){
int maxLeftSum, maxRightSum; //
int maxLeftBorderSum, maxRightBorderSum;
int leftBorderSum, rightBorderSum;
//递归结束的条件是当只有一个元素时,如果大于零便返回这个数,否则返回零
if(left == right)
{
if(A[left] > 0)
return A[left];
else
return 0;
}
//取序列中值进行划分
int mid = (left + right) / 2, i;
//递归调用左半部分,求出左边最大值
maxLeftSum = maxSubArraySum(A, left, mid);
//递归调用右半部分,求出右边最大值
maxRightSum = maxSubArraySum(A, mid + 1, right);
//求横跨序列两边的最大值=横跨序列前半部分的和+横跨序列后半部分的和
//求出横跨序列前半部分的和
maxLeftBorderSum = 0, leftBorderSum = 0;
for(i = mid; i >= left; i--){
leftBorderSum += A[i];
if(leftBorderSum > maxLeftBorderSum)
maxLeftBorderSum = leftBorderSum;
}
//求出横跨序列后半部分的和
maxRightBorderSum = 0, rightBorderSum = 0;
for(i = mid + 1; i <= right; i++){
rightBorderSum += A[i];
if(rightBorderSum > maxRightBorderSum)
maxRightBorderSum = rightBorderSum;
}
//返回三个值中的最大值即为最大子序列和
return maxNum(maxLeftSum, maxRightSum, maxLeftBorderSum + maxRightBorderSum);
}