连续子数组最大和
输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。
要求时间复杂度为O(n)。
示例1:
输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
提示:
1 <= arr.length <= 10^5
-100 <= arr[i] <= 100
动态规划解析:
大致思路:定义一个和目标数组num一样大小的动态规划数组dp,遍历后dp[i]的值为以元素num[i]为结尾的连续子数组最大和,实现的原理如下
状态定义:
1.设动态规划列表dp , dp[i] 代表以元素nums[i]为结尾的连续子数组最大和。
为何定义最大和dp[i]中必须包含元素nums[i] :保证dp[i]递推到dp[i + 1]的正确性;
如果不包含nums[i] ,递推时则不满足题目的连续子数组要求。
2.转移方程:若dp[i-1]≤0,说明dp[i- 1]对dp[i]产生负贡献,即dp[i - 1] + nums[i]还不如nums[i]本身大。
当dp[i-1]> 0时:执行dp[i] = dp[i- 1] + nums[i] ;
当dp[i -1]≤0时:执行dp[i]= nums[i];
3.初始状态:
dp[0] = nums[0],即以nums[0]结尾的连续子数组最大和为nums[0]。
4.返回值:
返回dp列表中的最大值,代表全局最大值。
public int maxSubArray1(int[] nums){
//暴力破解2优化,O(n^2)
int n = -100;
for (int i = 1; i < nums.length; i++) {
int sum = 0;
for (int j = i; j <= nums.length; j++){
sum += nums[j];
if (sum > n){
n = sum;
}
}
}
return n;
}
public int maxSubArray2(int[] nums){
//动态规划
//创建一个同样大小的数组dp[i]表示以元素num[i]为结尾的连续子数组最大和
int[] dp = new int[nums.length];
dp[0]=nums[0];//初始化dp[0]
for(int j = 1;j<nums.length;j++){
//判断条件,dp[j-1]>0表示以元素num[i]为结尾的连续子数组最大和大于0
// 即可对之后组成更大的连续子数组有正贡献
//dp[j-1]<0,表示以元素num[i]为结尾的连续子数组最大和小于0,
// 不再参与之后的连续子数组,子数组从新积累
if(dp[j-1]>0){
dp[j] = dp[j-1]+nums[j];
}else{
dp[j] = nums[j];
}
}
int max = Integer.MIN_VALUE;
for(int i = 0;i<dp.length;i++){
//遍历dp,找到最大值
if(dp[i]>max)
max = dp[i];
}
return max;
}
public int maxSubArray3(int[] nums) {
//同样的功能大佬的代码简单到离谱,阿巴阿巴
int res = nums[0];
for(int i = 1; i < nums.length; i++) {
nums[i] += Math.max(nums[i - 1], 0);
res = Math.max(res, nums[i]);
}
return res;
}
public class MaxSubArray {
public static void main(String[] args) {
Solution solution = new Solution();
int[] arr = new int[]{
-2,1,-3,4,-1,2,1,-5,4};//结果为6,ok
System.out.println(solution.maxSubArray3(arr));
}
}
总结:动态规划入门,这道题开始连最简单的暴力破解都没独立写出来,现在对这些解题思路豁然开朗,太厉害了。
暴力破解是双重循环比较每个子数组和的大小,从0开始[0],[0,1],[0,1,2],一直到[0,1,2,…n],然后再从1开始,[1],[1,2],[1,2,3]一直到[1,2,…n],然后再从2开始…
sum(0,0) | ||
sum(0,1) | sum(1,1) | |
sum(0,2) | sum(1,2) | sum(2,2) |
sum(0,3) | sum(1,3) | sum(2,3) |
… | … | … |
--------------------------------------->
而此题的动态规划思路是先找到每个以num[i]结尾的数组的最大子数组和存入数组dp,再遍历dp找到最大值
最大值 | |||
---|---|---|---|
sum(0,0) | dp[0] | ||
sum(0,1) | sum(1,1) | dp[1] | |
sum(0,2) | sum(1,2) | sum(2,2) | dp[2] |
sum(0,3) | sum(1,3) | sum(2,3) | dp[3] |
… | … | … | dp[j] |
最大值dp[j]的计算方法
dp[j] = dp[j-1]+nums[j]; dp[j-1]>0
dp[j] = nums[j]; dp[j-1]<0
自己用例子计算一遍给出的例子就能够理解这个方法