解法一:前缀和
子数组的和等于两个前缀和的差
func maxSubArray(nums []int) int {
if len(nums) == 1{
return nums[0]
}
// 要求子数组最少包含一个元素,因此不存在0个数的前缀和
preSumList := make([]int, len(nums))
for i, v := range nums{
if i == 0{
preSumList[i] = v
}else{
preSumList[i] = preSumList[i-1] + v
}
}
// 当前的前缀和 - 最小前缀和,不断比较,找出和最大的连续子数组
minSum := 0 // 初始值为0,一开始并不知道哪个前缀和最小,那遍历到的第一个就不需要做相减了,减去0相当于本身,找到最大的那个前缀和即可
res := math.MinInt
for _, v := range preSumList{
res = max(res, v - minSum)
minSum = min(minSum, v)
}
return res
}
func max(a, b int) int{
if a > b {
return a
}
return b
}
func min(a, b int) int{
if a < b {
return a
}
return b
}
- 时间复杂度:O(n)
- 空间复杂度:O(n)
优化空间复杂度
实际上,这题只关注最大值,不关心子数组索引起止,我们无需维护一个整个前缀和数组,我们可以一边遍历数组计算前缀和,一边维护前缀和的最小值,用当前的前缀和减去前缀和的最小值,就得到了以当前元素结尾的子数组和的最大值,用它来更新答案的最大值。
注意,由于题目要求子数组不能为空,应当先计算前缀和-最小前缀和,再更新最小前缀和。
如果先更新最小前缀和,再计算前缀和-最小前缀和,就可能出现当前的前缀和恰好就是最小前缀和,那么会把空数组的元素和即 0算入答案,而题目已经要求了子数组至少包含一个元素
func maxSubArray(nums []int) int {
if len(nums) == 1{
return nums[0]
}
preSumMin := 0
res := math.MinInt
curPreSum := 0
for _, v := range nums{
curPreSum += v // 当前索引的前缀和
res = max(res, curPreSum-preSumMin) // 更新连续子数组和的最大值
preSumMin = min(preSumMin, curPreSum) // 更新前缀和最小值
}
return res
}
func max(a, b int) int{
if a > b {
return a
}
return b
}
func min(a, b int) int{
if a < b {
return a
}
return b
}
- 时间复杂度:O(n)
- 空间复杂度:O(1)