53 最大子序和(分治、贪心、动态规划)

265 篇文章 5 订阅
96 篇文章 2 订阅

1. 问题描述:

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

示例:

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

如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-subarray

2. 思路分析:

① 这道题目是一道经典的题目,一开始的时候比容易想到的是暴力破解,使用三层循环就可以解决,但是提交上去超时了,于是逛了一下领扣的官方题解,发现其中提供的三种方法都是写的很好的,非常值得学习,于是我看懂了官方的思路之后自己实现了一遍

② 第一种方法是分治法,分治本质上为递归,以数组的中间位置递归数组的左右两边,从而得到数组左右两边的最大值,除了求得两边的最大值之外我们还需要求解出左右两边为端点以中间位置作为分割点范围的最大值,因为可能以左右两边为边界得到的和是最大的,我们在计算以左右两边为范围的最大值需要传入三个变量,左边界,右边界与中间的分割点,我们需要分别计算中间点到左边界的最大值,中间点之后到右边界的最大值,最后将两部分的结果进行相加得到结果,官方提供的图解比较清楚地展示了整个的求解过程:

为什么要传入中间的分割点呢?而不是简单地将左右边界范围内的结果相加呢?这是因为我们有可能不是整个左右边界范围的和是最大的但是是以这个范围内的一个小范围是最大的,比如像这个数据:-1 2 3最大的范围是[2,3]所以我们需要计算出左边范围累加的最大值与右边范围的最大值,然后将两部分结果相加那么得到的就是整个范围的最大值

注意在计算整个范围的最大值的时候需要注意要以中间的分割点开始计算左边范围的最大值,不能以左边范围到中间分割点计算最大值,因为左边范围到中间的分割点有可能不是最大的,比如像上面的例子-1 2 3就是,假如我们计算以0-2范围内的最大值,假如从0-1开始累加那么得到的最大结果是1,而这个结果是错误的,假如从中间的分割点1到左边范围0进行累加那么得到的结果是2而这个结果才是正确的,所以这个问题要注意一下

③  除了使用分治的方法理解之外还可以贪心的方法来解决,每一次都是尝试将当前的位置的元素与之前累加的最大和进行相加看一下得到的结果是否更大,更大则更新最大值,其中的方法三使用的动态规划也是借助于贪心的思路来实现的

④ 最好使用debug并且结合具体的例子会更好理解一点,其实当自己有思路的时候先自己实现一遍,然后通过代码的前提下进一步优化代码,假如没有思路的话看别人的代码学习一下也是很有帮助的,看懂了之后自己实现一遍对于提升代码能力还是比较好的,特别是在领扣中有很多的大佬们的解法都是很棒的,非常值得学习

3. 代码如下:

分治代码:

class Solution {
    public int maxSubArray(int[] nums) {
        return recursion(0, nums.length - 1, nums);
    }

    private int recursion(int l, int r, int[] nums) {
        if (l == r) return nums[l];
        int p = (l + r) / 2;
        int leftSum = recursion(l, p, nums);
        int rightSum = recursion(p + 1, r, nums);
        /*需要计算出整个数组的最大值即可*/
        int totalSum = helper(l, p, r, nums);
        return Math.max(Math.max(leftSum, rightSum), totalSum);
    }

    private int helper(int l, int p, int r, int[] nums) {
        int sum = 0;
        int curleftmax = Integer.MIN_VALUE;
        int cur = 0;
        /*需要注意这里只能够从p到l的方向进行计算不能够从l到p的方向计算因为在可能出现3 -1 2 3*/
        for (int i = p; i >= l; --i){
            cur += nums[i];
            curleftmax = Math.max(cur, curleftmax);
        }
        cur = 0;
        int currightmax = Integer.MIN_VALUE;
        for (int i = p + 1; i <= r; ++i){
            cur += nums[i];
            currightmax = Math.max(cur, currightmax);
        }
        return curleftmax + currightmax;
    }
}

官方贪心代码:

class Solution {
  public int maxSubArray(int[] nums) {
    int n = nums.length;
    int currSum = nums[0], maxSum = nums[0];
    for(int i = 1; i < n; ++i) {
      currSum = Math.max(nums[i], currSum + nums[i]);
      maxSum = Math.max(maxSum, currSum);
    }
    return maxSum;
  }
}

官方动态规划代码:

class Solution {
  public int maxSubArray(int[] nums) {
    int n = nums.length, maxSum = nums[0];
    for(int i = 1; i < n; ++i) {
      if (nums[i - 1] > 0) nums[i] += nums[i - 1];
      maxSum = Math.max(nums[i], maxSum);
    }
    return maxSum;
  }
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值