53. 最大子数组和(剑指 Offer 42. 连续子数组的最大和)

题目:

思路:

【1】贪心的思路

【2】进阶的分治法

它不仅可以解决区间 [0,n−1],还可以用于解决任意的子区间 [l,r] 的问题。
如果我们把 [0,n−1] 分治下去出现的所有子区间的信息都用堆式存储的方式记忆化下来,即建成一棵真正的树之后,
我们就可以在 O(log⁡n)的时间内求到任意区间内的答案,
我们甚至可以修改序列中的值,做一些简单的维护,之后仍然可以在 O(log⁡n) 的时间内求到任意区间内的答案,
对于大规模查询的情况下,这种方法的优势便体现了出来

 

代码展示:

分治法的代码实现:

//时间7 ms击败5.5%
//内存50.8 MB击败48.95%
//时间复杂度:假设我们把递归的过程看作是一颗二叉树的先序遍历,
//那么这颗二叉树的深度的渐进上界为 O(log⁡n),这里的总时间相当于遍历这颗二叉树的所有节点,
//故总时间的渐进上界是 O(n),故渐进时间复杂度为 O(n)。
//空间复杂度:递归会使用 O(log⁡n)的栈空间,故渐进空间复杂度为 O(log⁡n)。
class Solution {
    public class Status {
        public int lSum, rSum, mSum, iSum;

        public Status(int lSum, int rSum, int mSum, int iSum) {
            // lSum 表示 [l,r] 内以 l 为左端点的最大子段和
            this.lSum = lSum;
            // rSum 表示 [l,r] 内以 rrr 为右端点的最大子段和
            this.rSum = rSum;
            // mSum 表示 [l,r] 内的最大子段和
            this.mSum = mSum;
            // iSum 表示 [l,r] 的区间和
            this.iSum = iSum;
        }
    }

    public int maxSubArray(int[] nums) {
        return getInfo(nums, 0, nums.length - 1).mSum;
    }

    public Status getInfo(int[] a, int l, int r) {
        if (l == r) {
            return new Status(a[l], a[l], a[l], a[l]);
        }
        // 进行递归二分
        int m = (l + r) >> 1;
        Status lSub = getInfo(a, l, m);
        Status rSub = getInfo(a, m + 1, r);
        // 将二分进行合并
        return pushUp(lSub, rSub);
    }

    // 数据示例【-2,1,-3,4,-1,2,1,-5,4】
    // 过程合并
    //合并前:Status{lSum=-2, rSum=-2, mSum=-2, iSum=-2} Status{lSum=1, rSum=1, mSum=1, iSum=1}
    //合并后:Status{lSum=-1, rSum=1, mSum=1, iSum=-1}
    //合并前:Status{lSum=-1, rSum=1, mSum=1, iSum=-1} Status{lSum=-3, rSum=-3, mSum=-3, iSum=-3}
    //合并后:Status{lSum=-1, rSum=-2, mSum=1, iSum=-4}
    //合并前:Status{lSum=4, rSum=4, mSum=4, iSum=4} Status{lSum=-1, rSum=-1, mSum=-1, iSum=-1}
    //合并后:Status{lSum=4, rSum=3, mSum=4, iSum=3}
    //合并前:Status{lSum=-1, rSum=-2, mSum=1, iSum=-4} Status{lSum=4, rSum=3, mSum=4, iSum=3}
    //合并后:Status{lSum=0, rSum=3, mSum=4, iSum=-1}
    //合并前:Status{lSum=2, rSum=2, mSum=2, iSum=2} Status{lSum=1, rSum=1, mSum=1, iSum=1}
    //合并后:Status{lSum=3, rSum=3, mSum=3, iSum=3}
    //合并前:Status{lSum=-5, rSum=-5, mSum=-5, iSum=-5} Status{lSum=4, rSum=4, mSum=4, iSum=4}
    //合并后:Status{lSum=-1, rSum=4, mSum=4, iSum=-1}
    //合并前:Status{lSum=3, rSum=3, mSum=3, iSum=3} Status{lSum=-1, rSum=4, mSum=4, iSum=-1}
    //合并后:Status{lSum=3, rSum=4, mSum=4, iSum=2}
    //合并前:Status{lSum=0, rSum=3, mSum=4, iSum=-1} Status{lSum=3, rSum=4, mSum=4, iSum=2}
    //合并后:Status{lSum=2, rSum=5, mSum=6, iSum=1}
    public Status pushUp(Status l, Status r) {
        int iSum = l.iSum + r.iSum;
        int lSum = Math.max(l.lSum, l.iSum + r.lSum);
        int rSum = Math.max(r.rSum, r.iSum + l.rSum);
        int mSum = Math.max(Math.max(l.mSum, r.mSum), l.rSum + r.lSum);
        return new Status(lSum, rSum, mSum, iSum);
    }
}

 

贪心的思路代码实现:

//时间1 ms击败100%
//内存50.2 MB击败94.22%
//时间复杂度:O(n),其中 n 为 nums 数组的长度。我们只需要遍历一遍数组即可求得答案。
//空间复杂度:O(1)。我们只需要常数空间存放若干变量。
class Solution {
    public int maxSubArray(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        int length = nums.length;
        int cur = nums[0];
        int max = nums[0];
        for (int i = 1; i < length; i++) {
            //这里如果之前的和为负数,那么则会被抛弃,改为取0,并加上下一个数而得到当前的和
            cur = Math.max(cur, 0) + nums[i];
            //记录最大值,判断当前的和与之前的最大数是否要进行替换
            max = Math.max(max, cur);
        }
        return max;
    }
}

 如果需要返回子序列数组的话:

public static int maxSubArray(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        int length = nums.length;
        int tab = 0;
        int cur = nums[0];
        int max = nums[0];
        for (int i = 1; i < length; i++) {
            cur = Math.max(cur, 0) + nums[i];
            //记录最大值
            max = Math.max(max, cur);
            if (nums[i] == cur && max == nums[i]){
                tab = i;
            }
        }
        //若是还需要返回对应的子序列数组的话,还可以添加以下代码
        ArrayList<Integer> arr = new ArrayList<>();
        int ma = max;
        for (int i = tab; i < length; i++) {
            arr.add(nums[i]);
            ma-=nums[i];
            if (ma == 0){
                System.out.println();
                break;
            }
        }
        return max;
    }

 

posted @ 2023-01-31 00:51  忧愁的chafry  阅读(11)  评论(0编辑  收藏  举报