参考链接:最大子段和详解 (各种复杂度)_风的记忆-CSDN博客_最大子段和
暴力求解(穷举、枚举)法O(n^2)
function getMaxSumEnum (arr) {
var max = arr[0];
var sum = 0;
for(var i=0; i<arr.length; i++) {
sum = 0;
for(var j=i; j<arr.length; j++) {
sum += arr[j];
if (sum > max) {
max = sum;
}
}
}
console.log(max);
return max;
}
var arr = [-1, 2, -3, 4, -1, 5];
getMaxSumEnum(arr);
// 输出
8
分支递归法 O(nlogn)
将数组划分为左右两部分,则最大子段和可能在三处出现:
用n表示最大索引,即len(arr)-1
- 左半部
- [0, n/2]
- 右半部
- [n/2+1, n]
- 跨越左右边界的部分
- 起点位于[0, n/2], 终点位于[n/2+1, n]
- 这种情况一定包含n/2和n/2+1这2个位置
- 可以使用枚举法
- 以n/2为终点,往左移动扩张,求出和最大的一个leftMax
- 以n/2+1为起点,往右移动扩张,求出和最大的一个rightMax
- leftMax + rightMax是这种情况可能的最大值
递归的终止条件是:left == right。
function getMaxSumDevideConquer (arr, left, right) {
// 思想:
// 将序列划分为左右两部分,则最大子段和可能在三处出现:
// 左半部、右半部以及跨越左右边界的部分。
// 递归的终止条件是:left == right。
if (left == right) {
return arr[left];
}
// 一半
var half = Math.floor((left + right) / 2);
// 左侧最大子段和 问题1:这里的左侧为什么也包含half这个中间值
var leftValue = getMaxSumDevideConquer(arr, left, half);
// 右侧最大字段和
var rightValue = getMaxSumDevideConquer(arr, half + 1, right);
// 中间最大字段和
var leftMax = arr[half];
var leftSum = arr[half];
for (i = half - 1; i >= left; i--) {
// 问题2:这里的左侧为什么也包含half这个中间值
leftSum += arr[i];
if (leftSum > leftMax) {
leftMax = leftSum;
}
}
var rightMax = arr[half + 1];
var rightSum = arr[half + 1];
for (var i = half + 1 + 1; i <= right; i++) {
// 问题3:这里的左侧为什么从half+1+1开始
rightSum += arr[i];
if (rightSum > rightMax) {
rightMax = rightSum
}
}
console.log(leftValue, rightValue, leftMax + rightMax);
return Math.max(leftValue, rightValue, leftMax + rightMax);
}
var arr = [-1, 2, -3, 4, -1, 5];
var max = getMaxSumDevideConquer(arr, 0, arr.length - 1)
console.log(max)
// 输出
-1 2 1
2 -3 -1
4 -1 3
4 5 8
2 8 7
8