题目描述#

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组是数组中的一个连续部分。

知识边界#

这道题主要涉及数组遍历前缀和最值维护

1. 数组与切片#

在 Go 中,这类题通常使用切片 []int 表示整数序列。

例如:

nums := []int{-2, 1, -3, 4, -1, 2, 1, -5, 4}

常见遍历方式:

for i := 0; i < len(nums); i++ {
    fmt.Println(nums[i])
}

for _, n := range nums {
    fmt.Println(n)
}

本题使用的是 for _, n := range nums 这种写法,适合顺序扫描数组中的每个元素。

2. 前缀和#

前缀和表示从数组开头到当前位置的元素总和。

设:

  • prefix[i] 表示前 i 个元素的和

那么任意连续子数组 nums[l...r] 的和可以表示为:

prefix[r+1] - prefix[l]

这就是本题做法的核心。只要我们在遍历到某个位置时,知道此前最小的前缀和,就可以立刻算出“以当前位置为右端点”的最大子数组和。

3. 最小前缀和维护#

代码中有三个关键变量:

  • prefix:当前前缀和
  • premin:遍历过程中出现过的最小前缀和
  • premax:当前找到的最大子数组和

核心过程是:

  1. 先更新当前前缀和
  2. prefix - premin 计算当前位置能得到的最大连续子数组和
  3. 再更新最小前缀和

因为固定当前右端点时,减去越小的前缀和,得到的区间和就越大。

4. Go 中的 math.MinInt#

代码中使用了:

premax := math.MinInt

这样做是为了保证即使数组中的元素全是负数,也能正确得到答案。

例如:

nums := []int{-3, -2, -5}

最大子数组和应该是 -2,如果把初始值写成 0,就会出错。

需要注意导入:

import "math"

5. 特殊情况处理#

单独处理了:

if len(nums) == 1 {
    return nums[0]
}

虽然这道题也可以不单独写这个分支,但按照当前代码逻辑保留这个判断,可以让单元素数组直接返回结果,逻辑更直接。

6. 常见错误点#

错误一:把答案初始值写成 0#

如果数组全为负数,答案不一定是 0,而应该是其中最大的那个负数。

错误二:更新顺序写错#

这里必须先:

prefix += n

然后用:

prefix - premin

更新答案,最后再更新 premin

如果顺序写错,可能会把当前前缀和错误地提前纳入最小前缀和中,影响结果。

错误三:把子数组理解成不连续的序列#

题目要求的是连续子数组,不允许跳过元素。

思路#

这道题按照你的代码思路,使用的是前缀和 + 历史最小前缀和的方法。

思路一#

设当前前缀和为 prefix

对于任意一个以当前位置结尾的连续子数组,它的和都可以表示为:

当前前缀和 - 某个之前位置的前缀和

如果想让这个值最大,那么减去的那个前缀和就应该尽可能小。因此在遍历数组时,我们维护一个变量 premin,表示到目前为止最小的前缀和。

同时维护:

  • premax:当前找到的最大子数组和
  • prefix:当前前缀和

遍历每个元素 n 时:

  1. n 加到 prefix
  2. 计算 prefix - premin,尝试更新 premax
  3. 如果当前 prefix 更小,就更新 premin

你的代码中还额外处理了 len(nums) == 1 的情况,这样单元素数组可以直接返回。

为什么这样做是正确的#

因为任意连续子数组都可以表示成两个前缀和之差。

当我们固定右端点时,子数组和是否最大,取决于左端点之前的前缀和是否最小。只要在扫描过程中持续维护“历史最小前缀和”,就能保证每次都找到当前右端点对应的最优子数组和。再在这些局部最优中取最大值,就得到全局最优答案。

时间复杂度#

O(n)

整个数组只遍历一次,每个元素只进行常数次操作。

空间复杂度#

O(1)

只使用了 premaxpreminprefix 三个额外变量。

代码#

package main

import "math"

func maxSubArray(nums []int) int {
	if len(nums) == 1 {
		return nums[0]
	}
	premax := math.MinInt
	premin := 0
	prefix := 0
	for _, n := range nums {
		prefix += n
		if prefix-premin > premax {
			premax = prefix - premin
		}
		if prefix < premin {
			premin = prefix
		}
	}
	return premax
}
本站总访问量  ·  访客数
你的IP 获取中…