5_53.最大子序和

题目描述:
image

解题思路:

  • 动态规划法:
    我们用 sums[i] 代表以第 i个数结尾的「连续子数组的最大和」,那么很显然我们要求的答案就是max(sums[i-1])+nums[i],nums[i]),这样就可以通过维护一个一维数组sums[i]来记录所有的以i为右端点的子序和。

  • 优化的动态规划法
    分析可知,由于sums[i]只和sums[i-1]有关,因此在计算maxValue的过程中,就会有很多冗余的数据,比如此时 i = 7,那么i = 0 ~ 5 这些个nums[i]都是没有必要维持的。因此只需要一个pre变量,来维护对于当前nums[i]nums[i-1]的值。
    对于pre变量的分析,可以先分析这个一维数组,有哪些数据是冗余的,在遍历循环的时候i++,pre就失效了,因此可以移动pre用来节省空间。

  • 分治法:
    我们定义一个操作 get(a, l, r) 表示查询 a 序列[l,r] 区间内的最大子段和,那么最终我们要求的答案就是 get(nums, 0, nums.size() - 1)
    分治的思想就是,先将这些分解成一个个的子空间,当分解到很小的子空间,这个子空间能解决问题了的时候,再对这些进行归并。此题就可以每次分一半,直到最后每个区间只剩下一个元素的时候,自然就是最优的。
    问题是:要维护这个区间的哪些信息,并且如何合并呢?
    对于一个区间 [l,r],我们可以维护四个量:

lSum 表示 [l,r] 内以 l 为左端点的最大子段和
rSum 表示 [l,r] 内以 r 为右端点的最大子段和
mSum 表示 [l,r] 内的最大子段和
iSum 表示 [l,r] 的区间和
然后通过左右子区间这些信息,来合并。

  • 扫描法:
    当我们加上一个正数时,和会增加;当加上一个负数时,和会减少。
    如果当得到的和是个负数,那么这个和在接下来的累加中应该抛弃并重新清零,不然会减少接下来的和。

代码:

动态规划法
class Solution {
    public int maxSubArray(int[] nums) {
        int len = nums.length;
        int[] sums = new int[len];
        sums[0] = nums[0];
        int maxValue = sums[0];
        for (int i = 1; i < len; i++){
            sums[i] = Math.max(nums[i],nums[i] + sums[i - 1]);
            maxValue = Math.max(sums[i],maxValue);
        }
        return maxValue;
    }
}
优化的动态规划
class Solution {
    public int maxSubArray(int[] nums) {
        int pre = 0;
        int maxValue = nums[0];    
        for (int i = 0; i < nums.length; i++){
            pre = Math.max(nums[i],pre + nums[i]);
            maxValue = Math.max(pre,maxValue);
        }
        return maxValue;
    }
}
分治法
class Solution {
    public int maxSubArray(int[] nums) {
        int left = 0;
        int right = nums.length - 1;
        return divide(nums,left,right).mSum;
    }
    public class Status{
        private int lSum,rSum,iSum,mSum;
        public Status(int lSum,int rSum,int mSum,int iSum){
            this.lSum = lSum;
            this.rSum = rSum;
            this.mSum = mSum;
            this.iSum = iSum;
        }
    }

    public Status divide(int[] nums,int left,int right){
        if (left == right){
            return new Status(nums[left],nums[left],nums[left],nums[left]);
        }
        int m = (left + right) >> 1;
        Status lSub = divide(nums,left,m);
        Status rSub = divide(nums,m + 1,right);
        return merge(lSub,rSub);
    }

    public Status merge(Status left,Status right){
        int iSum = left.iSum + right.iSum;
        int lSum = Math.max(left.lSum,left.iSum + right.lSum);
        int rSum = Math.max(right.rSum,left.rSum + right.iSum);
        int mSum = Math.max(Math.max(left.mSum,right.mSum),left.rSum + right.lSum);
        return new Status(lSum,rSum,mSum,iSum);
    }
}
扫描法
class Solution {
    public int maxSubArray(int[] nums) {
        int sum = 0;
        int maxValue = nums[0];
        for (int i = 0; i < nums.length; i++){
            if (sum + nums[i] < 0){
                sum = 0;
                maxValue = Math.max(maxValue,nums[i]);
            }else{
                sum = sum + nums[i];
                maxValue = Math.max(sum,maxValue);
            }
        }
        return maxValue;
    }
}
posted @ 2021-10-20 23:04  Forrestyu  阅读(54)  评论(1)    收藏  举报