1. 问题
一个有 n 个元素的数组,这 n 个元素既可以是正数也可以是负数,数组中连续的一个或者多个元素可以组成一个连续的子数组,一个数组可能有多个连续的子数组,求子数组的最大值。如数组 [1, -2, 4, 8, -4, 7, -1, -5] 其最大和的子数组为 [4, 8 -4, 7],最大值为 15。
2. 思路实现
2.1 暴力法
依次遍历生成所有子数组,并求出子数组的和,然后取最大值就是题目要求。
package main
import (
"fmt"
"math"
)
func getMax(a []int) int {
max := math.MinInt64
for i := 0; i < len(a); i++ {
sum := a[i]
for j := i + 1; j < len(a); j++ {
sum += a[j]
if sum > max {
max = sum
}
}
}
return max
}
func main() {
arr := []int{1, -2, 4, 8, -4, 7, -1, -5}
fmt.Println(getMax(arr))
}
2.2 动态规划思想
首先可以根据数组最后一个元素 a[n-1] 与最大子数组的关系可以分为以下 3 种情况:
- 最大子数组包含 a[n-1] ,即最大子数组以 a[n-1] 结尾
- a[n-1] 单独构成最大子数组
- 最大子数组不包含 a[n-1],那么求 a[1…n-1] 的最大子数组可以转换为求 a[1…n-2] 的最大子数组
上面分析可以得到以下结论:
假设已经计算出子数组 a[1…i-2] 的最大子数组的和为 all[i-2],同时也计算出 a[0…i-1] 中包含 a[i-1] 的最大的子数组和为 end[i-1]。则可以得出如下关系:all[i-1]=max{end[i-1], a[i-1], all[i-1]}
package main
import (
"fmt"
)
func Max(a, b int) int {
if a > b {
return a
} else {
return b
}
}
func getMax(a []int) int {
end := make([]int, len(a))
all := make([]int, len(a))
end[0] = a[0]
all[0] = a[0]
end[len(a)-1] = a[len(a)-1]
all[len(a)-1] = a[len(a)-1]
for i := 1; i < len(a); i++ {
end[i] = Max(end[i-1]+a[i], a[i])
all[i] = Max(end[i], all[i-1])
}
return all[len(a)-1]
}
func main() {
arr := []int{1, -2, 4, 8, -4, 7, -1, -5}
fmt.Println(getMax(arr))
}
改进的动态规划
func getMax(a []int) int {
end := a[0] // 最大子数组和
all := a[0] // 包含最后一个元素的最大子数组和
for _, v := range a {
end = Max(end+v, v)
all = Max(end, all)
}
return all
}