问题简介
题目描述
给你一个整数数组 nums,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。
📌 示例说明
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6。
示例 2:
输入:nums = [1]
输出:1
示例 3:
输入:nums = [5,4,-1,7,8]
输出:23
💡 解题思路
方法一:动态规划(Kadane 算法)✅(推荐)
这是解决最大子数组和问题最经典、最高效的方法。
思路步骤:
- 定义状态 :设
dp[i]表示以nums[i]结尾的最大子数组和。 - 状态转移方程 :
- 如果
dp[i-1] > 0,那么dp[i] = dp[i-1] + nums[i](加上前面的正收益) - 否则
dp[i] = nums[i](从当前重新开始) - 可简化为:
dp[i] = max(nums[i], dp[i-1] + nums[i])
- 如果
- 初始化 :
dp[0] = nums[0] - 结果 :所有
dp[i]中的最大值
实际上我们不需要保存整个
dp数组,只需维护一个变量记录当前最大子数组和即可,空间复杂度可优化到 O(1)。
方法二:分治法(了解即可)
将数组分为左右两半,最大子数组和可能出现在:
- 左半部分
- 右半部分
- 跨越中点的部分
递归求解左右部分,再计算跨越中点的最大和,取三者最大值。
时间复杂度 O(n log n),不如 Kadane 算法高效,但有助于理解分治思想。
方法三:暴力枚举(❌ 不推荐)
双重循环枚举所有子数组,计算和并更新最大值。
时间复杂度 O(n²),在 LeetCode 上会超时。
💻 代码实现
java
// Java 实现 - 动态规划(Kadane 算法)
class Solution {
public int maxSubArray(int[] nums) {
int maxSum = nums[0]; // 全局最大和
int currentSum = nums[0]; // 当前子数组和
for (int i = 1; i < nums.length; i++) {
// 决定是继续累加还是从当前元素重新开始
currentSum = Math.max(nums[i], currentSum + nums[i]);
// 更新全局最大值
maxSum = Math.max(maxSum, currentSum);
}
return maxSum;
}
}
go
// Go 实现 - 动态规划(Kadane 算法)
func maxSubArray(nums []int) int {
maxSum := nums[0] // 全局最大和
currentSum := nums[0] // 当前子数组和
for i := 1; i < len(nums); i++ {
// 决定是继续累加还是从当前元素重新开始
if currentSum+nums[i] > nums[i] {
currentSum += nums[i]
} else {
currentSum = nums[i]
}
// 更新全局最大值
if currentSum > maxSum {
maxSum = currentSum
}
}
return maxSum
}
🧪 示例演示
以 nums = [-2,1,-3,4,-1,2,1,-5,4] 为例:
| i | nums[i] | currentSum(当前子数组和) | maxSum(全局最大) |
|---|---|---|---|
| 0 | -2 | -2 | -2 |
| 1 | 1 | 1 | 1 |
| 2 | -3 | -2 | 1 |
| 3 | 4 | 4 | 4 |
| 4 | -1 | 3 | 4 |
| 5 | 2 | 5 | 5 |
| 6 | 1 | 6 | 6 |
| 7 | -5 | 1 | 6 |
| 8 | 4 | 5 | 6 |
✅ 最终返回 6,对应子数组 [4, -1, 2, 1]
✅ 答案有效性证明
数学归纳法简要证明 Kadane 算法正确性:
- 基础情况:当数组只有一个元素时,算法返回该元素,显然正确。
- 归纳假设 :假设对于前
k个元素,currentSum正确表示以第k个元素结尾的最大子数组和。 - 归纳步骤 :考虑第
k+1个元素:- 若
currentSum > 0,加上nums[k+1]一定比单独nums[k+1]更优; - 若
currentSum ≤ 0,则从nums[k+1]重新开始更优。 - 因此
currentSum = max(nums[k+1], currentSum + nums[k+1])正确。
- 若
同时,maxSum 始终记录历史最大值,故最终结果正确。
📊 复杂度分析
| 方法 | 时间复杂度 | 空间复杂度 | 是否最优 |
|---|---|---|---|
| 动态规划(Kadane) | O(n) | O(1) | ✅ 是 |
| 分治法 | O(n log n) | O(log n) | ❌ 否 |
| 暴力枚举 | O(n²) | O(1) | ❌ 否 |
✅ Kadane 算法是最优解:线性时间,常数空间。
📌 问题总结
- 核心思想:贪心 + 动态规划。只要当前累计和为正,就值得保留;否则果断舍弃,从新位置开始。
- 关键洞察:最大子数组和一定是以某个位置结尾的子数组中的最大值。
- 应用场景:股票最大收益(一次交易)、信号处理中的峰值检测等。
- 扩展思考 :
- 如果要求返回子数组的起止索引?→ 可额外记录起始位置。
- 如果数组全为负数?→ 算法仍有效,返回最大负数。
- 环形数组最大子数组和?→ LeetCode 918,需结合总和与最小子数组和。
💡 记住 Kadane 算法模板 :
current = max(nums[i], current + nums[i])
global_max = max(global_max, current)
github地址: https://github.com/swf2020/LeetCode-Hot100-Solutions