这是我参与更文挑战的第2天,活动详情查看: 更文挑战
原题
给定一个整数数组 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
示例 4:
输入:nums = [-1]
输出:-1
进阶: 如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的 分治法 求解。
重拳出击
并不能快速出击,这简单题怎么这么难啊,淦。
首先仔细看了看题目,关键字是连续子数组
暴力
正经人能想到是暴力解法。 一看题目,是个数组,那么就能通过循环来获得它的连续子数组(不需要拿出来所有的子数组,那样的话实在太暴力了),写出来应该是一个双层for循环。max的初始值要足够的小,不要设置0,防止意外发生。
int max = Integer.MIN_VALUE;
for (int i = 0; i < nums.length; i++)
{
int sum = 0;
for (int j = i; j < nums.length; j++)
{
sum = sum + nums[j];
//因为取的是连续子数组的和,所以这里一个一个往后头加,然后找出来和最大的就行了
if (sum > max) max = sum;
}
}
//循环结束后max的值就是本题的答案
^正常情况到这就结束了,但是字数不太够,没得法子^
动态规划
一看题目下面有个标签[动态规划],发现这题居然还能用动态规划,它不写我就不知道。菜主要就是菜在这里。
然后开始套动态规划题目的板子去写:
-
边界条件
题目写了
nums.length>=1那这个就没什么特殊的边界了,就记一下第一个值用来推算就行。 -
子问题
一个长度为 n 的数组,它最大和的连续子数组的结尾可能在中间,也可能在两端。这波把子问题设置为:以nums[i]结尾的最大子数组
然后人脑推算一下,这是一个长度为
m (0 < m << n)的数组,假如以它第m-1个元素为结尾的子数组是最大的。那么,往它后面走一格,长度为m + 1,这时候最大连续子数组的和。就会与第m个元素有关,这个元素可能大于之前那个连续子数组或者小于它 。这样,就会有两个结果:- 子数组和比
m大,继续比较第m + 1个元素 - 子数组和比
m小,放弃子数组,从第m个元素重新累计
上头这个结论是能够一直往后面计算的,直到 m = n 。最终留下的那个子数组的和就是最大的。
- 子数组和比
-
方程
根据上一条,可以得到如下:
当数组
nums[i]遍历结束之后,会得到一个dp[i]数组中存的是以nums[i]结尾的最大子数组的和,为了得到答案,需要在遍历一次。 -
结合各种条件写代码
public int maxSubArray(int[] nums) { //动态规划嘛 if (nums.length == 1) return nums[0]; //状态:以num[i]结尾的最大子数组 为 dp[i] int[] dp=new int[nums.length]; dp[0] = nums[0]; //状态转移方程:MAX(nums[i], dp[i-1]+nums[i]) for (int i=1;i<nums.length;i++){ dp[i] = Math.max(nums[i],dp[i-1]+nums[i]); } //遍历dp数组找到最大的那个 //初始值设置成最小的,有可能数组里头全是负数 int res = Integer.MIN_VALUE; for (int j : dp) { res = Math.max(res, j); } return res; }
收
- 其实那个子问题还能以第
m个元素是否大于零去做判断,又是一种不太一样的方法 - 其实我这个答案还能进行空间优化
- 其实原题目要我们用分治法,但是我不会