求解最大子数组
暴力破解法
通过遍历每个子数组,找到最小的子数组
算法复杂度 $$n^2$$
1 | func FindMaxSubArr(A []int) (int, int, int) { |
分治法
分解数组规模,递归的求解左右子数组及跨越中间的数组。
将数组分为两部分,最大子数组要么在左边的子数组,要么在右边的子数组,要么跨越两个子数组
算法复杂度 $$n log(n)$$
1 | func FindMaxSubArr(A []int) (int, int, int) { |
动态规划
若已知 A[1.. j] 的最 大子数组,基于如下性质将解扩展为 A[1. .j+1] 的最大子数组: A[1. .j+1] 的最大子数组要么是 A[1.. j] 的最大子数组,要么是某个子数组 A[i.. j+1])
简单的理解,给一个已知最大子数组的数组增加一个单元,新数组的最大子数组如果产生了变化,会有两种情况:
- 最大子数组不变
- 最大子数组内包含新增的单元
初版错误的代码
1 | func FindMaxSubArr(A []int) (int, int, int) { |
随机测试发现 类似序列 [-8 -4 6 -8 5 7 -5 6 -1 -7] 并不能正常生成最大子数组。
问题在于左界只会因为之前的子数组小于当前单元时会右移重置,会导致计算的最大子数组中包含了左侧连续的和为负的子数组,导致整体的和变小。
解决这个问题,我们需要知道。 x < 0, x + P <= P
我们需要动态的舍去已经计算出的负数子数组。当前子数组的和已经小于零,那么不论下一个单元是正是负,都会大于或者等于当前的子数组的合。
1 | else if leftToNow < 0 { |
再次测试的时候,发现还是有问题,问题出在这两行
1 | if leftToNow >= max && leftToNow > A[i] |
我们仅当 leftNow > A[i] 时才更新 max, 但是通过上面的代码,我们可能已经将leftToNow 更新成了 A[i] , 因此它将永远不会成立。
我们只需要将两个条件中的任意一个 leftToNow 与 A[i] 的比较进行 == 的判断就可以了
算法复杂度 $$n$$
1 | func FindMaxSubArr(A []int) (int, int, int) { |
实际上 如果采用
1 | } else if A[i] >= leftToNow && A[i] > max { |
tempLeft = i + 1 也是可以省略的