[Java] LeeCode 最大子序和

最大子序和问题

动态规划、分治法

题目

在这里插入图片描述根据题目可知,9个数字共有9+8+7+……+1=45种解法,如果遍历求最大值,时间复杂度为O^2,这个速度太慢。

解法一:动态规划

我们把上面的45种组合进行分类,分解成数量较少的几个子问题:
1.以第一个数字结尾的连续序列:[-2],最大值-2。
2.以第二个数字结尾的连续序列:[-2,1],[1],最大值1。
3.以第三个数字结尾的连续序列……
如果能得到这几个序列的最大值,那么比较这9个子组合的最大值就可以得到整体的最大值了。比较组合二和组合三,可以得到规律即:第n个组合只是在第(n-1)个组合的基础上每一个数组增加第n个数字,然后再增加数组[n]。将第n个组合分成两种情况:
1.继承n-1组合得到的;
2.新生成的,即[n];
从加和的角度来说,假设第n个数字是负数,那么n无法给前面n-1个组合带来正增长,那么第n-1个组合就会抛弃第n个数字,自己就是最大的。
伪代码:if max(n-1)>0,max(n)=max(n-1)+nums(n);
else max(n)=nums(n) ;

也就是说,我们只需要一个变量来保存前面子组合的最大值。另一个变量保存全局最大值。
代码如下:

  public int maxSubArray(int[] nums) {
        if (nums == null) {
            return 0;
        }
       int max = nums[0];    // 全局最大值
        int subMax = nums[0];  // 前一个子组合的最大值
        for (int i = 1; i < nums.length; i++) {
            if (subMax > 0) {
                // 前一个子组合最大值大于0,正增益
                subMax = subMax + nums[i];
            } else {
                // 前一个子组合最大值小于0,抛弃前面的结果
                subMax = nums[i];
            }
     // 计算全局最大值
            max = Math.max(max, subMax);
        }
      return max;
    }

解法二:分治法

分治法就是将整个数组划分成几个小组,每个小组再划分成几个更小的小组,一直到只剩下一个小组为止。每个小组会计算出最优值,汇报给上一级的小组,上级根据小组的汇报比较爱得出最大值。
这个问题的关键在于如何划分使得每个小组之间没有重复的组合,这个数组[-2,1,-3,4,-1,2,1,-5,4]一共有9个元素,根据center=(start+end)/2这个原则,得到中间元素的索引为4.也就是-1,拆分成三个组合,
[-2,1,-3,4,-1]一组;
[2,1,-5,4]一组;
包含左边序列最右边一组和右边序列最左边元素的序列;
代码如下:

public int maxSubArray(int[] nums) {
        return maxSubArrayDivideWithBorder(nums, 0, nums.length-1);
    }
  private int maxSubArrayDivideWithBorder(int[] nums, int start, int end) {
        if (start == end) {
            // 只有一个元素,也就是递归的结束情况
            return nums[start];
        }
    int center = (start + end) / 2;
        int leftMax = maxSubArrayDivideWithBorder(nums, start, center); // 计算左侧子序列最大值
        int rightMax = maxSubArrayDivideWithBorder(nums, center + 1, end); // 计算右侧子序列最大值

        // 计算包含左侧子序列最后一个元素的子序列最大值
        int leftCrossMax = Integer.MIN_VALUE; // 初始化一个值
        int leftCrossSum = 0;
        for (int i = center ; i >= start ; i --) {
            leftCrossSum += nums[i];
            leftCrossMax = Math.max(leftCrossSum, leftCrossMax);
        }
         int rightCrossMax = nums[center+1];
        int rightCrossSum = 0;
        for (int i = center + 1; i <= end ; i ++) {
            rightCrossSum += nums[i];
            rightCrossMax = Math.max(rightCrossSum, rightCrossMax);
        }
    int crossMax = leftCrossMax + rightCrossMax;
    return Math.max(crossMax, Math.max(leftMax, rightMax));
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值