LeetCode53 最大子数组和

82 阅读2分钟

leetcode.cn/problems/ma… image.png

解法一:前缀和

子数组的和等于两个前缀和的差

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算入答案,而题目已经要求了子数组至少包含一个元素

image.png

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)