LeedCode53 最大子序和

求最大子序列的和

题目链接:leetcode53 最大子序和
本文主要讲述两种方法:贪心算法和分治法

一、贪心算法

样例数据:int[] a = new int[]{-2,1,-3,4,-1,2,1,-5,4};

简单考虑,先假设最大子序列的和肯定大于等于0。
从左往右遍历数组

  1. 对于第一个数,a[0] = -2 < 0;假设最大子序列包含a[0],最大子序列的最右边坐标为j,那么最大子序列的和为a[0] + a[1] + … + a[j],因为a[0] < 0;所以a[1] + … + a[j] 肯定大于 a[0] + a[1] + … + a[j];所以a[0]肯定不会出现在最大子序列中。
  2. 对于第一个数a[1] = 1。当前的最大值为1,先记录下来,此时max = 1,tempMax = 1;
  3. 对于第二个数a[2] = -3 。a[1] + a[2] = 1 + -3 = -2 < 0;根据1得出的结论,如果最大子序列包含a[1]和a[2]或者仅仅包含a[2]的话,其实是对求最大子序列和是起到负作用的。所以我们这时候把tempMax置为0。
  4. 对于a[3] = 4, tempMax = 4, max = 4;
  5. 对于a[4] = -1, tempMax = 3, max = 4;
  6. 对于a[5] = 2, tempMax = 5, max = 5;
  7. 对于a[6] = 1, tempMax = 6, max = 6;
  8. 对于a[7] = -5, tempMax = -1, max = 6;
  9. 对于a[8] = 4, tempMax = 3, max = 6;

综上所述,我们最最大子序列的时候,可以从左往右遍历,进行累加。如果累加的结果出现负值,这个时候前面的和对于后续求最大子序列和是起到反作用的。最大子序列要么出现在这个累加的过程中,要么就出现在后续的没处理的数组中。
这块比较难以理解,举个例子来说明,对于数组[1,2,3,-3,-6,2,3,5]
对于[0,2]最大值为6,
对于[0,4]和为-3,在计算[5,7]的和中,左边的数据实际上产生的是负的作用。应该被舍弃。

至于出现负值重置为0,是因为对于[1,2,3,-4,1,2]来说
[0,2]的和为6
a[3]虽然小于0,但是a[0]+a[1]+a[2]+a[3] > 0,对于3后面的数值来说,仍然是正的,具有正作用。所以继续累加。

附上代码

public static int maxSubArray(int[] nums) {
        if (nums == null || nums.length < 0) {
            return 0;
        }
        int sum = 0;
        int max = nums[0];  //这里主要是为了防止整个数列中的所有数都小于0
        for (int i = 0; i < nums.length; i++) {
            sum += nums[i];
            if (max < sum) {
                max = sum;
            }
            if (sum < 0) {
                sum = 0;
            }

        }
        return max;
    }

二、分治法

分治法的核心思想就是分而治之。
对于一个数组求最大子序列和,无外乎有三种情况
通过二分的方式把数组一分为二,分别为[left, mid]、[mid + 1, right]

  1. 最大子序列仅出现在左边
  2. 最大子序列仅出现在右边
  3. 最大子序列横跨左右两边

对于前两种情况,直接一直递归下去求解就好啦
对于第三种情况。横跨左右两边,说明肯定包含两个元素 [mid] [mid+1]
我们从mid开始,求出包含mid的左边最大的子序列和,
从mid + 1开始,求出mid+1的右边最大的子序列和
然后返回的值就是这三个值中最大的那个。

附上代码

public static int maxSubArray(int[] nums, int left, int right) {
        if (left == right) {
            return nums[left];
        }
        int mid = (left + right) / 2;
        int leftMaxSum = maxSubArray(nums, left, mid);
        int rightMaxSum = maxSubArray(nums, mid + 1, right);
        int allMaxSum = 0;
        int temp = nums[mid];
        int tempMax = temp;
        for (int i = mid - 1; i >= left; i--) {
            temp += nums[i];
            if (tempMax < temp) {
                tempMax = temp;
            }
        }
        allMaxSum += tempMax;
        temp = nums[mid + 1];
        tempMax = temp;
        for (int i = mid + 2; i <= right; i++) {
            temp += nums[i];
            if (tempMax < temp) {
                tempMax = temp;
            }
        }
        allMaxSum += tempMax;
        return getMax(leftMaxSum, rightMaxSum, allMaxSum);
    }

    public static int getMax(int a, int b, int c) {
        if (a > b) {
            return a > c ? a : c;
        }
        return b > c ? b : c;
    }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值