问题描述:给定长度为n的整数序列,a[1...n], 求[1,n]某个子区间[i , j]使得a[i]+…+a[j]和最大.或者求出最大的这个和.例如(-2,11,-4,13,-5,2)的最大子段和为20,所求子区间为[2,4].
求子区间及其最大值,是非常适合采用分治法德算法设计思想来设计的,其分治的思想是把一个难以直接解决的大问题,分成一些规模较小的相同性质的问题,以便各个击破,分而治之。如果规模为N的问题可以分解成k个子问题(1<k<=N),并且这些子问题之间相互独立,互不影响,递归地解决这些问题,然后合并这些子问题的解,得到最后问题的解。
分治法的代码实现过程如下:
int MaxSubSum(int a[],int left,int right)
{
int sum = 0; //用来存储最大子段和
if (left == right) //只有一个元素
{
sum = a[left] > 0 ? a[left] : 0;
}
else
{
int center = (left + right)/2;
int leftsum = MaxSubSum(a,left,center);
int rightsum = MaxSubSum(a,center+1,right);
//下面是第三种情况
int s1 = 0,lefts = 0;
for (int i = center; i >= left; i --)
{
lefts += a[i];
if (lefts > s1)
s1 = lefts;
}
int s2 = 0,rights = 0;
for (i = center+1; i <= right; i ++)
{
rights += a[i];
if (rights > s2)
s2 = rights;
}
sum = s1 + s2;
if (sum < leftsum)
sum = leftsum;
if (sum < rightsum)
sum = rightsum;
}
return sum;
}
这个函数的时间复杂度是O(n*log n) ,那能不能找到一个O(N)时间复杂度,O(1)空间复杂度的算法呢?并且加大问题的难度,能够返回子数组的开始和结束的索引,并且能够处理数组全是负数的情况,当全是负数时,返回最大的那么元素值作为最大值,并且找到这个数所在的索引?答案是肯定的,在这里就要借助于动态规划算法来解决。
其基本思路如下:
用b记录当前的最大值
nMax记录最大值,即返回值
1、如果b>0,则b+a[i];如果b<=0,则b=a[i]
2、若b>nMax,则nMax=b,即更新最大值,否则最大值不更新
//标示输入数据是否有效
static bool bInvalidInput = false;
int MaxSubSum3(int *a, int nCount,int &nStart,int &nEnd)
{
if (a == NULL || nCount <= 0)
{
bInvalidInput = true;
return -1;
}
//初始化前后的索引
nStart = 0;
nEnd = 0;
if (1 == nCount)
{
return a[0];
}
int nSubMax = a[0]; //当前最大值
int nMax = nSubMax; //子数组之和最大值
for (int i = 1; i < nCount; i ++)
{
if (nSubMax > 0)
{
nSubMax += a[i];
}
else if (nSubMax <= 0)
{
if (a[i] > nSubMax)
{
nSubMax = a[i];
nStart ++;
}
}
//更新最大值
if (nSubMax > nMax)
{
nMax = nSubMax;
nEnd = i;
}
}
return nMax;
}