算法设计与分析——最大子段和(分治)

一、问题描述

对应的力扣练习:https://leetcode-cn.com/problems/lian-xu-zi-shu-zu-de-zui-da-he-lcof/

Description

给定有n个整数(可能为负整数)组成的序列a1,a2,...,an,求该序列连续的子段和的最大值。 如果该子段的所有元素和是负整数时定义其最大子段和为0。

Input

第一行有一个正整数n(n<1000),后面跟n个整数,绝对值都小于10000。直到文件结束。

Output

输出它的最大子段和。

Sample Input

6 -2 11 -4 13 -5 -2

Sample Output

24

解决该问题有很多方法可以通过暴力、动态规划和分治,这里使用分治的方法来解决该题目。

二、分治策略

img

用分治法求解这个问题 。

在数组的 center = (right-left)/2+left 位置处分开。形成两个子数组。

那么,最大子段和可能出现在三个位置:

  1. 可能出现在左子数组
  2. 可能出现在 右子数组
  3. 可能出现在 过center的中间某部分元素组成的子数组。

下面考虑 三种情况的计算方法:

第一种情况: 计算 left 到 center 的最大和,记作 leftSum

第二种情况: 计算从 center+1 到 right的最大和,记作 rightSum

第三种情况: 跨边界的和。 以center为中心分别向两边计算和。

      a.从 center出发,每次向左边扩张一步,并且记录当前的值S1,如果当前的和比上次的和大,就更新S1,一直向左扩张到位置 Left。

      b.从 center+1出发,每次扩张一步,计算当前的和 为S2,如果当前的值比上次的和 大,那么,就更新S2的值,一直向右扩张到位置Right。

      c.计算过center的连续值的和,S1+S2的值 Sum。 这个就是跨边界的和。

上面三种情况考虑计算完成后,最后一步就是,比较三个值中的最大值,取最大值就可以了。

时间复杂度

我们在(left+right)/2处,把大问题分成了两个部分的小问题。

在跨边界求和的时候,我们需要计算 从center出发的到 left方向的最大和,每次增加一个元素,通过比较,得到最大的和,时间复杂度为O(N),同样, 从center+1 出发的到 right 方向的最大和,每次增加一个元素,通过比较,得到最大的和,时间复杂度 为 O(N) ,所以,时间复杂度为 O(N)。

img

伪代码

img

Java源码

class Solution {
   // 分治法
    public int maxSubArray(int[] nums) {
      return divideConquer(nums,0,nums.length-1);
    }

    public int divideConquer(int[] nums,int low,int high) {
        //递归出口
        if (low>=high){
            return nums[low];
        }
        //三种情况,第一种:数组在左区间
        //第二种:数组在又区间
        //第三种:数组横跨左右区间
        //前两种递归求解即可
        //第三种情况以中间位置向两周扩散,找到最大值
        int mid = low+(high-low)/2;
        int left = divideConquer(nums,low,mid-1);
        int right = divideConquer(nums,mid+1,high);
        int maxMid = nums[mid];
        int curMId = nums[mid];
        for (int i = mid-1;i>=low;i--){
            curMId += nums[i];
            if (maxMid<curMId) maxMid = curMId;
        }
        curMId = maxMid;
        for (int i = mid+1;i<=high;i++){
            curMId += nums[i];
            if (maxMid<curMId) maxMid = curMId;
        }
        //4.返回三种情况中的最大值
        return Math.max(Math.max(left,right),maxMid);
    }
}
posted @ 2019-09-17 17:28  王陸  阅读(12562)  评论(1编辑  收藏  举报