LeetCode刷题--53. 最大子序和(简单)
题目描述
给定一个整数数组 nums
,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
进阶:
如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。
方法一:动态规划
首先需要把这个问题分解成最优子问题来解。最主要的思路就是将上面的45个组合进行
,分解成数量较少的几个子问题。在这里我们一共有9个数字,顺理成章的我们把组合分解成9个小组的组合。
第一个子组合是以第一个数字结尾的连续序列,也就是 [-2],最大值-2
第二个子组合是以第二个数字结尾的连续序列,也就是 [-2,1], [1],最大值1
第三个子组合是以第三个数字结尾的连续序列,也就是 [-2,1,3], [1,3], [3],最大值4
以此类推。。。
如果我们能够得到每一个子组合的最优解,也就是子序列的最大值,整体的最大值就可以通过比较这9个子组合的最大值来得到了。
现在我们找到了最优子问题,重叠子问题在哪呢?那就得细心比较一下每个子问题。
从第二个子组合和第三个子组合可以看到,组合 3 只是在组合 2 的基础上每一个数组后面添加第 3 个数字,也就是数字 3,然后增加一个只有第三个数字的数组 [3] 。
这样两个组合之间的关系就出现了,可是我们不关心这个序列是怎么生成的,只是关心最大值之间的关系。
- 下面我们看组合 3 的组成,我们将子组合 3 分成两种情况:
- 继承子组合二得到的序列,也就是[-2,1,3], [1,3] (最大值 1 = 第二个组合的最大值 + 第三个数字)
- 单独第三个数字的序列,也就是[3] (最大值 2 = 第三个数字)
如果 第二个序列的最大值 大于0,那么最大值 1 就比最大值 2 要大,反之最大值 2 较大。
这样,我们就通过第二个组合的最大值和第三个数字,就得到了第三个组合的最大值。因为第二个组合的结果被重复用到了,所以符合这个重叠子问题的定义。
通俗来讲这个问题就变成了,第 i 个子组合的最大值可以通过第i-1个子组合的最大值和第 i 个数字获得,如果第 i-1 个子组合的最大值没法给第 i 个
数字带来正增益,我们就抛弃掉前面的子组合,自己就是最大的了。
代码实现
class Solution {
public int maxSubArray(int[] nums) {
int len = nums.length;//数组长度
//如果数组长度为0,直接返回0
if (len == 0) {
return 0;
}
int[] dp = new int[len];//创建一个数组dp,长度为 原数组的长度
dp[0] = nums[0];//让元素组的第一个元素等于dp数组第一个元素
for (int i = 1; i < len; i++) {
//如果第 i-1 个子组合的最大值可以给第 i 个数字带来正增益,就两个组合相加。
if (dp[i - 1] >= 0) {
dp[i] = dp[i - 1] + nums[i];
//反之我们就抛弃掉前面的子组合,自己就是最大的了。
} else {
dp[i] = nums[i];
}
}
// 最后不要忘记全部遍历一遍,求最大值
int ans = dp[0];
for (int i = 1; i < len; i++) {
ans = Math.max(ans, dp[i]);
}
return ran;
}
}
- 状态压缩 , 来看看代码,我们只需要一个变量sum保存前面子组合的最大值,另外一个ans保存全局最大值。
public int maxSubArray(int[] nums) {
int ans = nums[0];
int sum = 0;
//动态规划的是首先对数组进行遍历,当前最大连续子序列和为 sum,结果为 ans
for(int num: nums) {
//如果 sum > 0,则说明 sum 对结果有增益效果,则 sum 保留并加上当前遍历数字
if(sum > 0) {
sum += num;
//如果 sum <= 0,则说明 sum 对结果无增益效果,需要舍弃,则 sum 直接更新为当前遍历数字
} else {
sum = num;
}
//每次比较 sum 和 ans的大小,将最大值置为ans,遍历结束返回结果
ans = Math.max(ans, sum);
}
return ans;
}