Loading

力扣 - 剑指 Offer 42. 连续子数组的最大和

题目

剑指 Offer 42. 连续子数组的最大和

思路1(分析数组的规律)

  • 我们可以从头到尾逐个累加,若之前的累加和小于0,那就从丢弃之前的累加,从当前开始重新累加,同时在遍历过程中比较记录下最大值
    • curSum记为当前最大值,为 0,以 [-2,1,-3,4,-1,2,1,-5,4] 为例:
      1. 首先加上 -2,此时 curSum 为 -2
      2. 由于 -2 小于 0,所以丢弃,然后再加上 1,此时 curSum 为 1
      3. 再加上 -3,此时 curSum 为 -2
      4. 由于 -2 小于 0,所以再次丢掉,然后加上 4,此时 curSum 为4
      5. 然后加上 -1,此时 curSum 为 3
      6. 再加上 2,此时 curSum 为 5
      7. 再加上 1,此时 curSum 为 6
      8. 再加上 -5,此时 curSum 为 1
      9. 最后再加上最后一个 4,此时 curSum 为 5
      10. 在这每次遍历中,我们使用一个变量 res 存储最大值,可以找到最大值为 6

代码

class Solution {
    public int maxSubArray(int[] nums) {

        int length = nums.length;
        // 最大总和值
        int res = Integer.MIN_VALUE;
        // 当前总和
        int curSum = 0;

        for (int i = 0; i < length; i++) {
            if (curSum < 0) {
                // 如果 i 之前总和值小于0,那就从 i 开始重新计算
                curSum = nums[i];
            } else {
                // 否则加上当前的值
                curSum += nums[i];
            }

            // 寻找最大值
            if (curSum > res) {
                res = curSum;
            }
        }

        return res;
    }
}

复杂度分析

  • 时间复杂度:\(O(N)\)
  • 空间复杂度:\(O(1)\)

思路2(动态规划)

  • 和思路一差不多动态规划就是利用历史记录,避免重复计算。所以也是从头到尾逐个累加,若之前的累加和小于0,那就从丢弃之前的累加,从当前开始重新累加,我们可以定义一个 dp 数组,dp[i] 代表的意义就是以 i 结尾的子数组的最大值。因此我们可以得出状态转移方程:

    \[dp[i] = \begin{cases} dp[i-1]+nums[i], & \text{if } dp[i-1]<0 \\ nums[i], & \text{if } dp[i-1]\geq0 \end{cases} \]

  • 既然我们可以得到以 i 结尾子数组的最大值,那么只需要从这些最大值中找到最大的一个就是结果了~

代码

class Solution {
    public int maxSubArray(int[] nums) {
        int length = nums.length;
        int[] dp = new int[length];
        dp[0] = nums[0];
        int res = dp[0];

        for (int i = 1; i < length; i++) {
            dp[i] = dp[i-1] > 0 ? dp[i-1]+nums[i] : nums[i];
            res = Math.max(res, dp[i]);
        }

        return res;
    }
}

可以进一步优化:

class Solution {
    public int maxSubArray(int[] nums) {
        int length = nums.length;
        // 记录子数组中的最大值
        int res = Integer.MIN_VALUE;
        // 记录前一段子数组之和
        int preSum = 0;

        for (int num : nums) {
            // 意思也是判断前一个字数组之和是否小于0
            preSum = Math.max(num, preSum + num);
            // 然后记录最大值
            res = Math.max(res, preSum);
        }

        return res;
    }
}

复杂度分析

  • 时间复杂度:\(O(N)\)
  • 空间复杂度:\(O(1)\)
posted @ 2021-11-27 18:20  linzeliang  阅读(66)  评论(0编辑  收藏  举报