一个有n个整数元素(可以是负数)的一维数组,连续的子数组有很多,那连续的子数组之和的最大值是什么呢?而这个和最大的子数组是哪一段数据呢?下面就让我们来一一的剖析这个问题。首先撇开后面一个问题,不考虑究竟是哪一段数据,只需要求出最大的和就可以了。
1、在说思路之前,先看看网上比较流行的解法,到底这里面有什么问题呢?如果数组里面有正数,OK,那一点问题都没有,比如说{-2,5,3,-6,4,-8,6},其最大值依然是8,但是如果全是负数呢?比如说{-5,-2,-1,-4,-6},正确答案应该是-1,但这里却只能得到0,。很明显算法有问题,或者说这种算法的适用度不是很广。
int ErrorGetMaxSubSum(int *Array, int nLen)
{
if(nLen < 1 || NULL == Array)
return Inf;
int b = 0;
int sum = 0;
int i;
for(i = 0; i < nLen; ++i)
{
//如果b<0,说明前面的数会对后面的和起负作用,故重新开始计数
if(b < 0)
{
b = Array[i];
}
else
{
//正常情况下就叠加
b += Array[i];
}
if(sum < b)
{
//如果b加的是正数,这一条一定为真。这样可以排除最后加入几个负数。
sum = b;
}
}
return sum;
}
2、下面我们来考虑一下几种情况。数组的第一个元素arr[0],以及最大的一段数组arr[i].....arr[j]跟arr[0]之间的关系,有以下几种情况:*当0=i=j时,元素arr[0]本身构成最大的一段;
*当0=i<j时,和最大的一段从arr[0]开始;
*当0<i时,元素arr[0]和最大的一段没有关系。
OK,那我们用一个sum来动态的记录某一段数据的和,用ans来记录曾经出现过的最大和。那么代码如下:
#include<stdio.h>
const int Inf = -1e5;
inline int max(const int a, const int b)
{
return a > b ? a : b;
}
//得到数组中的连续最大和
int GetMaxSubSum(int *arr, int nLen)
{
if(!arr || nLen < 1)
return Inf;
int ans = arr[0];
int sum = arr[0];
for(int i = 1; i < nLen; ++i)
{
if(sum < 0)
sum = 0;
sum += arr[i];
if(ans < sum)
ans = sum;
}
return ans;
}
//上面那个函数的更加精炼的版本
int GetMaxSubSumEx(int *arr, int nLen)
{
if(!arr || nLen < 1)
return Inf;
int ans = arr[0];
int sum = arr[0];
for(int i = 1; i < nLen; ++i)
{
sum = max(arr[i], sum + arr[i]);
ans = max(ans, sum);
}
return ans;
}
3、如果说在给出最大和的同时,需要知道是哪一段数据构成了这个最大和。那么每一次sum被重置为0的时候,就可能是新的数据段的开始,用temp记下。每一次用sum来更新ans的时候,就说明当前这一段数据是最优的,当前这一段数据是[temp,i],所以更新[b,e]。//在得到数组最大和的同时,得到是哪一段数据
int GetMaxSubSum(int *arr, int nLen, int &b, int &e)
{
if(!arr || nLen < 1)
return Inf;
int ans = arr[0];
int sum = arr[0];
int temp = 0;
b = e = 0;
for(int i = 1; i < nLen; ++i)
{
if(sum < 0)
{
sum = 0;
temp = i;
}
sum += arr[i];
if(ans < sum)
{
ans = sum;
b = temp;
e = i;
}
}
return ans;
}
//打印数组
void Print(int *arr, int nLen)
{
if(!arr|| nLen < 1)
{
return;
}
int i;
for(i = 0; i < nLen; ++i)
{
printf("%d ", arr[i]);
}
printf("\n");
}
4、照例,最后给出main函数的调用方法。仅供参考,不需要者可以pass掉。int main()
{
const int N = 20;
int arr[N];
int i,n,ans;
int b,e;
while(scanf("%d", &n) != EOF)
{
for(i = 0; i < n; ++i)
scanf("%d", &arr[i]);
ans = GetMaxSubSum(arr, n);//方法1的调用
printf("最大和为:%d\n", ans);
ans = GetMaxSubSumEx(arr, n);//方法1等价形式的调用
printf("最大和为:%d\n", ans);
ans = ErrorGetMaxSubSum(arr, n);//错误方法的调用
printf("最大和为:%d\n", ans);
ans = GetMaxSubSum(arr, n, b, e);//方法1扩展的调用
printf("最大和为:%d\n数据段为:", ans);
Print(&arr[b], e - b + 1);
}
}
最后给出几组测试用例的结果,当然其中有一个是调用错误的方法得到的错误结果,一眼就可以看到啦!