这是我参与8月更文挑战的第20天,活动详情查看:8月更文挑战
前言
力扣第五十三题 最大子序和
如下所示:
给定一个整数数组 nums
,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例 1:
输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6 。
示例 2:
输入: nums = [1]
输出: 1
示例 3:
输入: nums = [0]
输出: 0
一、思路
最大子序和
中有两个比较的信息:
- 连续子数组
- 最大和
这一题我开始是用双层遍历来的,击败率只有 7%
。于是就看了下官方的解法,发现动态规划是一个非常不错的解法。下面会主要介绍动态规划,双层遍历会放在解法一中。
动态规划
动态规划中最重要的一点就是:以每个位置为终点的最大连续子数组都是基于其前一位置的最大子数组计算得出
假设
sum[i]
为以i
为结尾的最大连续子数组的和0
很显然能够得到 sum[i] = max{sum[i-1] + nums[i], nums[i]}
,因为对于 nums[i]
来说要么加入它前面的那一段组成最大子数组,要么选择自己一个人成为最大子数组。
如何理解
sum[i] = max{sum[i-1] + nums[i], nums[i]}
呢?假设nums = {-2, 1}
来说,易知sum[0] = -2
,那么sum[1]
等于多少呢?很显然此时
nums[1]
作为独立的最大子数组才能够让和最大,所以此时sum[1] = nums[1]
通过上面的步骤我们找到了所有以 i
为结尾的最大子数组和。然后再遍历这个数组,返回最大值即可!
举个例子
此处以 nums = {-2,1,-3,4,-1,2,1,-5,4}
作为例子
- 初始化
sum[0] = nums[0]
- 通过
sum[i] = Math.max(sum[i-1] + nums[i], nums[i])
找到所有的sum[i]
。遍历结束后,sum[] = {-2, 1, -2, 4, 3, 5, 6, 1, 5}
- 返回
sum[]
中的最大值6
即可
二、实现
实现代码
实现代码与思路保持一致,虽然提供了两种解题方法。但是依然推荐使用动态规划来实现!
双重遍历
/**
* 双层循环
*/
public int maxSubArray(int[] nums) {
int ret = nums[0];
int len = nums.length;
for (int i=0; i<len; i++) {
int sum = nums[i];
ret = Math.max(ret, sum);
for (int j=i+1; j<len; j++) {
sum = sum + nums[j];
ret = Math.max(ret, sum);
}
}
return ret;
}
动态规划
public int maxSubArray(int[] nums) {
int[] sum = new int[nums.length];
sum[0] = nums[0];
// 动态规划找到所有以 i 为结尾的最大值
for (int i = 1; i<nums.length; i++) {
sum[i] = Math.max(sum[i-1] + nums[i], nums[i]);
}
// 返回最大值
int ret = sum[0];
for (int s : sum) {
ret = Math.max(s, ret);
}
return ret;
}
测试代码
public static void main(String[] args) {
int[] nums = {-2,1,-3,4,-1,2,1,-5,4};
new Number53().maxSubArray(nums);
}
结果
三、总结
如果有人仔细看的话,会发现我的动态规划相较于力扣的官方题解多了一层循环,来找出 sum[]
数组中的最大值。力扣官方的动态规划相当于是一个简化版本(省略了比对得到最大值,且只是用了一个 sum
常数来存储大小),不便于初学者理解,所以我就没有放上来了,还望理解!
感谢看到最后,非常荣幸能够帮助到你~♥