动态规划or分治法——力扣53.最大子数组和
力扣53.最大子数组和

【LeetCode 53】最大子数组和(Java 详细题解 + 动态规划 + 分治法拓展)
一、题目描述
给定一个整数数组 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
提示:
- 1 <= nums.length <= 10⁵
- -10⁴ <= nums[i] <= 10⁴
进阶:
如果你已经实现 O(n) 的解法,请尝试使用分治法实现。
二、思路分析
这道题的核心思想是:
对于数组中的每一个元素,判断“以它结尾的最大子数组和”是多少。
换句话说,我们在遍历数组时,需要不断地记录“当前最大连续和”,并实时更新“全局最大值”。
1. 动态规划思路(Kadane 算法)
设 dp[i] 表示 以第 i 个元素结尾的最大连续子数组和,则有:
dp[i] = max(nums[i], dp[i-1] + nums[i])
解释:
- 如果
dp[i-1] + nums[i]比nums[i]大,说明把当前元素接到前面的子数组更优; - 否则,从当前元素重新开始计算。
最终答案就是所有 dp[i] 中的最大值。
三、代码实现(Java)
class Solution {public int maxSubArray(int[] nums) {// 边界条件if (nums == null || nums.length == 0) {return 0;}int currentSum = nums[0]; // 当前子数组的最大和int maxSum = 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;}
}

四、复杂度分析
| 项目 | 分析 |
|---|---|
| 时间复杂度 | O(n),只需一次遍历数组 |
| 空间复杂度 | O(1),只使用常数级变量保存当前和与最大和 |
五、举例讲解
以 nums = [-2,1,-3,4,-1,2,1,-5,4] 为例:
| i | nums[i] | currentSum 计算 | currentSum | maxSum |
|---|---|---|---|---|
| 0 | -2 | 初始化 | -2 | -2 |
| 1 | 1 | max(1, -2+1)=1 | 1 | 1 |
| 2 | -3 | max(-3, 1-3)=-2 | -2 | 1 |
| 3 | 4 | max(4, -2+4)=4 | 4 | 4 |
| 4 | -1 | max(-1, 4-1)=3 | 3 | 4 |
| 5 | 2 | max(2, 3+2)=5 | 5 | 5 |
| 6 | 1 | max(1, 5+1)=6 | 6 | 6 |
| 7 | -5 | max(-5, 6-5)=1 | 1 | 6 |
| 8 | 4 | max(4, 1+4)=5 | 5 | 6 |
最终结果:maxSum = 6
六、进阶:分治法(Divide and Conquer)
除了线性动态规划,还可以使用分治法解决。
思想
将数组分为左右两部分:
- 最大子数组要么在左半部分;
- 要么在右半部分;
- 要么跨越中点。
我们递归计算左右部分的最大子数组和,再计算中间跨越部分的最大和。
实现思路简要
class Solution {public int maxSubArray(int[] nums) {return divide(nums, 0, nums.length - 1);}private int divide(int[] nums, int left, int right) {if (left == right) return nums[left];int mid = (left + right) / 2;int leftSum = divide(nums, left, mid);int rightSum = divide(nums, mid + 1, right);int crossSum = cross(nums, left, right, mid);return Math.max(Math.max(leftSum, rightSum), crossSum);}private int cross(int[] nums, int left, int right, int mid) {int leftMax = Integer.MIN_VALUE;int sum = 0;for (int i = mid; i >= left; i--) {sum += nums[i];leftMax = Math.max(leftMax, sum);}int rightMax = Integer.MIN_VALUE;sum = 0;for (int i = mid + 1; i <= right; i++) {sum += nums[i];rightMax = Math.max(rightMax, sum);}return leftMax + rightMax;}
}

复杂度分析:
- 时间复杂度:O(n log n)
- 空间复杂度:O(log n)(递归栈)
七、总结对比
| 方法 | 思路 | 时间复杂度 | 空间复杂度 | 特点 |
|---|---|---|---|---|
| 动态规划(Kadane) | 从左到右线性扫描 | O(n) | O(1) | 简洁高效 |
| 分治法 | 递归拆分区间 | O(n log n) | O(log n) | 理论性强、适合理解问题结构 |
八、总结
本题是经典的动态规划入门题,掌握后能帮助你理解一类“区间连续最优问题”。
无论是求最大子数组和,还是其他类似的最大连续区间问题,Kadane 算法都是最重要的基础算法之一。
