最大子序列和问题(分治法Java实现)
所谓最大子序列和问题,本节讨论的是求数组中最大连续子序列的和。
例如给定数组 A={6,3,-11, 5, 8, 15, -2, 9, 10, -5},则最大连续子序列的和为 45,即 5+8+15+(-2)+ 9+10 = 45。
当 n>1 时,可利用分治法求解该问题,令 mid=(left+right)/2,最大子序列的和可能出现在以下 3 个区间内:
1) 该子序列完全落在左半区间,即 a[0…mid-1]中,可采用递归将问题缩小在左半区间,通过调用自身 maxLeftSum = MaxSubSum(a,left,mid)求出最大连续子序列的和 maxLeftSum。
2) 该子序列完全落在右半区间,即 [mid…n-1]中,类似地,可通过调用自身 maxRightSum =MaxSubSum(a,mid,right) 求出最大连续子序列的和 maxRightSum。
3) 该子序列落在两个区间之间,横跨左右两个区间,需要从左半区间求出:
从右半区间求出:
最大连续子序列的和为
最后需要求出这 3 种情况连续子序列的和的最大值,即 maxLeftSum1+maxRightSum1,maxLeftSum, maxRightSum 的最大值就是最大连续子序列的和。
递归调用自身求左半区间的最大连续子序列的和 maxLeftSum:
递归调用自身求右半区间最大连续子序列的和 maxRightSum:
求左半区间中从 mid-1 到 i 的最大子序列的和 maxLeftSum1:
求右半区间从 mid 到 i 的最大子序列的和 maxRightSum1:
求以上 3 种情况的最大值,即最大连续子序列的和:
例如给定数组 A={6,3,-11, 5, 8, 15, -2, 9, 10, -5},则最大连续子序列的和为 45,即 5+8+15+(-2)+ 9+10 = 45。
最大子序列和问题解析
假设要求子序列的和至少包含一个元素,对于含 n 个整数的数组 a[],若 n=1,则表示该数组中只有一个元素,返回该元素。当 n>1 时,可利用分治法求解该问题,令 mid=(left+right)/2,最大子序列的和可能出现在以下 3 个区间内:
1) 该子序列完全落在左半区间,即 a[0…mid-1]中,可采用递归将问题缩小在左半区间,通过调用自身 maxLeftSum = MaxSubSum(a,left,mid)求出最大连续子序列的和 maxLeftSum。
2) 该子序列完全落在右半区间,即 [mid…n-1]中,类似地,可通过调用自身 maxRightSum =MaxSubSum(a,mid,right) 求出最大连续子序列的和 maxRightSum。
3) 该子序列落在两个区间之间,横跨左右两个区间,需要从左半区间求出:

从右半区间求出:

最大连续子序列的和为
maxLeftSum1+ maxRightSum1
。最后需要求出这 3 种情况连续子序列的和的最大值,即 maxLeftSum1+maxRightSum1,maxLeftSum, maxRightSum 的最大值就是最大连续子序列的和。
分治法解决最大子序列和问题
算法实现如下:public class MaxSubSum { int GetMaxNum(int x, int y, int z) { if (x > y && x > z) return x; if (y > x && y > z) return y; return z; } int GetMaxSubSum(int a[], int left, int right) { int mid, maxLeftSum, maxRightSum, i, tempLeftSum, tempRightSum; int maxLeftSum1, maxRightSum1; if (right - left == 1) { // 如果当前序列只有一个元素 return a[left]; } mid = (left + right) / 2; // 计算当前序列的中间位置 maxLeftSum = GetMaxSubSum(a, left, mid); maxRightSum = GetMaxSubSum(a, mid, right); // 计算左边界最大子序列的和 tempLeftSum = 0; maxLeftSum1 = a[mid - 1]; for (i = mid - 1; i >= left; i--) { tempLeftSum += a[i]; if (maxLeftSum1 < tempLeftSum) maxLeftSum1 = tempLeftSum; } // 计算右边界最大子序列的和 tempRightSum = 0; maxRightSum1 = a[mid]; for (i = mid; i < right; i++) { tempRightSum += a[i]; if (maxRightSum1 < tempRightSum) maxRightSum1 = tempRightSum; } // 返回当前序列最大子序列的和 return GetMaxNum(maxLeftSum1 + maxRightSum1, maxLeftSum, maxRightSum); } public static void main(String args[]) { MaxSubSum S = new MaxSubSum(); int a[] = {6, 3, -11, 5, 8, 15, -2, 9, 10, -5}, s; System.out.println("元素序列: "); for (int i = 0; i < a.length; i++) System.out.printf("%5d", a[i]); System.out.println(); s = S.GetMaxSubSum(a, 0, a.length); System.out.printf("最大连续子序列的和:%d\n", s); } }程序运行结果如下:
元素序列: 6 3 -11 5 8 15 -2 9 10 -5 最大连续子序列的和:45下面简要分析以上代码。若子序列中只有一个元素,则返回该元素,即:
if (right - left == 1) { // 若当前子序列只有一个元素 return a[left]; }
递归调用自身求左半区间的最大连续子序列的和 maxLeftSum:
maxLeftSum = GetMaxSubSum(a,left,mid);
递归调用自身求右半区间最大连续子序列的和 maxRightSum:
maxRightSum = GetMaxSubSum(a,mid,right);
求左半区间中从 mid-1 到 i 的最大子序列的和 maxLeftSum1:
tempLeftSum = 0; maxLeftSum1 = a[mid-1]; for (i = mid - 1; i >= left; i--) { tempLeftSum += a[i]; if (maxLeftSum1 < tempLeftSum) maxLeftSum1 = tempLeftSum; }
求右半区间从 mid 到 i 的最大子序列的和 maxRightSum1:
tempRightSum = 0; maxRightSum1 = a[mid]; for (i = mid; i < right; i++) { tempRightSum += a[i]; if (maxRightSum1 < tempRightSum) maxRightSum1 = tempRightSum; }
求以上 3 种情况的最大值,即最大连续子序列的和:
return GetMaxNum(maxLeftSum1 + maxRightSum1, maxLeftSum, maxRightSum);