【刷题日记】最大子数组和

111 阅读4分钟

1、 题目描述

LeetCode 53.最大子数组和

😉给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组是数组中的一个连续部分。

示例 1:

输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

示例 2:

输入:nums = [1]
输出:1

2、思路

思路历程

一眼动态规划!为什么呢?

动态规划的定义:全局最优可通过组合子问题的最优解得到,说人话就是全局的最优解可以通过一步步的局部最优解累积得到。
带入本题,全局的最大子数组和其实就是

  • 首先计算以第一个数组元素为边界的子数组最大值 sum1

  • 然后计算以第二个数组元素为边界的子数组最大值 sum2

  • 然后计算以第三个数组元素为边界的子数组最大值 sum3

  • 。。。

  • 然后计算以第n个数组元素为边界的子数组最大值 sum n

得到动态规划递推逻辑:
最大子数组和 = 到前一个索引位的最大子数组和 + 当前索引位的元素,或者是当前索引位元素,,通俗来讲就是 选不选当前索引位元素,因为对于当前元素来讲 只有选与不选的可能,我们比较的就是选后大 、还是不选后大
动态规划递推公式:
dp[ i ] = Math.max ( dp[ i - 1 ] + num[ i ] , num[ i ] )
其中 i 表示 第 i 个数组索引位的 子数组和最大值是多少。

动态规划的本质:以一定的规则通过局部求解,从而慢慢逻辑推理出全局的解
举例说明:

前两个元素最大字数组和 = 前一个元素的最大子数组和 与选、不选 第二个元素比较大小
sum1 = Math.max ( sum0 + num1 , num1 )

前三个元素最大子数组和 = 前两个元素最大字数组和 与选、不选 第三个元素比较大小
sum2 = Math.max ( sum1 + num2 , num2 )

3、代码

class Solution {
    public int maxSubArray(int[] nums) {
        // 动态规划 dp 数组
        int[] dp = new int[nums.length];
        // 初始化第一个元素,此时表示 索引位为0的最大子数组和为 nums[0]
        dp[0] = nums[0];
        // 存储全局最大子数组和
        int globalMax = nums[0];
        // 由于初始化了0,所以从1号位开始遍历计算
        for(int i = 1; i < nums.length; i++){
            // 递推公式的应用 
            dp[i] = Math.max(dp[i-1] + nums[i],nums[i]);
            // 比较得到当前的最大子数组和
            globalMax = Math.max(dp[i],globalMax);
        }
        return globalMax;
    }
}

4、算法分析

复杂度分析

时间复杂度: 只遍历了一遍数组,总时间复杂度为 O(n)
空间复杂度: 创建了与输入数组相同的数组,总空间复杂度O(n)。还有优化空间
实际执行耗时与内存占用率:

image.png 5、总结

动态规划是一种常见的解题思路,掌握简单的题解是重要的。简单的来说动态规划就是找到 递推公式,代码实现递推公式。大多数动态规划都可以归类为 选 还是 不选的逻辑,以便于理解。

6、tips

上述实现的代码中 ,为了更清晰的讲清楚动态规划,所以按照动态规划的模版模式 1.定义dp数组 ,2.从小到大推理dp数组值,3.得到全局解。对于上述题目来讲 dp数组实际只用到了 当前元素的前一个值(增加了堆空间内存使用),频繁调用 max方法(增加了栈空间内存使用),频繁比较、赋值globalMax (增加了时间复杂度)。

那么总结下来还有三个优化点:

  1. 只需要一个变量记住前一个索引位数组最大值就行

  2. 减少方法调用

  3. 短路比较与更新

代码实现

class Solution {
    public int maxSubArray(int[] nums) {
        // 全局最大子数组和
        int globalMax = nums[0];
        // 前一个元素最大子数组和
        int preIndexMax = nums[0];
        for (int i = 1; i < nums.length; i++) {
            preIndexMax = preIndexMax < 0 ? nums[i] : preIndexMax + nums[i];
            // 短路更新
            if(preIndexMax > globalMax){
                globalMax = preIndexMax;
            }
        }
        return globalMax;
    }
}

在这里插入图片描述

建议关注同名微信公众号 以便及时收到最新更新~