LeetCode01连续子数组最大和(动态规划)

连续子数组最大和

输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。

要求时间复杂度为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

自己用例子计算一遍给出的例子就能够理解这个方法

猜你喜欢

转载自blog.csdn.net/weixin_44529429/article/details/114136531
今日推荐