输入一组整数,求出这组数字子序列和中最大值。也就是只要求出最大子序列的和,不必求出最大的那个序列。例如:
序列:-2 11 -4 13 -5 -2,则最大子序列和为20。
序列:-6 2 4 -7 5 3 2 -1 6 -9 10 -2,则最大子序列和为16。
1.对这个问题,有一个相对复杂的O(NlogN)的解法,就是使用递归。该方法我们采用“分治策略”(divide-and-conquer)。
在我们例子中,最大子序列可能在三个地方出现,或者在左半部,或者在右半部,或者跨越输入数据的中部而占据左右两部分。前两种情况递归求解,第三种情况的最大和可以通过求出前半部分最大和(包含前半部分最后一个元素)以及后半部分最大和(包含后半部分的第一个元素)相加而得到。
//递归法,复杂度是O(nlogn) long maxSumRec(const vector<int>& a, int left, int right) { if (left == right) { if (a[left] > 0) return a[left]; else return 0; } int center = (left + right) / 2; long maxLeftSum = maxSumRec(a, left, center); long maxRightSum = maxSumRec(a, center+1, right); //求出以左边对后一个数字结尾的序列最大值 long maxLeftBorderSum = 0, leftBorderSum = 0; for (int i = center; i >= left; i--) { leftBorderSum += a[i]; if (leftBorderSum > maxLeftBorderSum) maxLeftBorderSum = leftBorderSum; } //求出以右边对后一个数字结尾的序列最大值 long maxRightBorderSum = 0, rightBorderSum = 0; for (int j = center+1; j <= right; j++) { rightBorderSum += a[j]; if (rightBorderSum > maxRightBorderSum) maxRightBorderSum = rightBorderSum; } return max3(maxLeftSum, maxRightSum, maxLeftBorderSum + maxRightBorderSum); } long maxSubSum3(const vector<int>& a) { return maxSumRec(a, 0, a.size()-1); }
2.
下面介绍一个线性的算法,这个算法是许多聪明算法的典型:运行时间是明显的,但是正确性则很不明显(不容易理解)。全部为负数的情况,特殊处理。
//线性的算法O(N)
long maxSubSum4(const vector<int>& a) { long maxSum = 0, thisSum = 0; for (int j = 0; j < a.size(); j++) { thisSum += a[j]; if (thisSum > maxSum) maxSum = thisSum; else if (thisSum < 0) thisSum = 0; } return maxSum; }