问题描述:给定长度为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; }