最大子数组问题介绍
算法导论上举了一个例子
实际上问题就是 寻找数组A的最大非空连续子数组
算法思路
分治法
假设将原数组一分为2的话,我们要寻找的最大子数组他所处的位置只有三种情况
1.最大子数组完全在左边
2.最大子数组完全在右边
3.最大子数组穿越左边和右边
然后我们只要比较这三个和的大小就可以知道最大的是哪一组了
算法过程
假定一个求解方法findMaxSubarray
,
求解左边最大子数组findMaxSubarray(left)
求解右边最大子数组findMaxSubarray(right)
我们还需要一个求得最大交叉数组的方法findMaxCrossingSubarray
过程如下图所示 从中间分别往两边遍历累加,分别记住两边和最大的位置然后在合并得到的就是交叉最大子数组
算法实现
javascript版本
function findMaxCrossingSubarray(left, right) {
const L = left.length
const R = right.length
let sum = left[L - 1]
let leftSum = left[L - 1], rightSum = right[0]
let maxLeft = L-1
let maxRight = 0
for (let i = L - 2; i >= 0; i--) {
sum = left[i] + sum
if (leftSum < sum) {
leftSum = sum
maxLeft = i
}
}
sum = right[0]
for (let j = 1; j < R; j++) {
sum = right[j] + sum
if (rightSum < sum) {
rightSum = sum
maxRight = j
}
}
return [left.slice(maxLeft, L).concat(right.slice(0, maxRight + 1)), leftSum + rightSum]
}
function findMaxSubarray(arr) {
let newArr = [...arr]
let len = newArr.length
if (len < 2) return [newArr, newArr[0]]
let middle = Math.floor(len / 2)
let left = newArr.slice(0, middle)
let right = newArr.slice(middle, len)
let [leftArr, leftSum] = findMaxSubarray(left)
let [rightArr, rightSum] = findMaxSubarray(right)
let [crossArr, crossSum] = findMaxCrossingSubarray(left, right)
if (leftSum >= rightSum && leftSum >= crossSum) {
return [leftArr, leftSum]
} else if (rightSum >= leftSum && rightSum >= crossSum) {
return [rightArr, rightSum]
} else {
return [crossArr, crossSum]
}
}
module.exports = findMaxSubarray