leetcode剑指offer42.连续子数组的最大和

题目描述

输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。

要求时间复杂度为O(n)。

题目难度:简单

** 示例1**

输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

提示

1 <= arr.length <= 10^5
-100 <= arr[i] <= 100

暂且忽略时间复杂度要求,这题看起来很熟悉,好像是我本科算法课上老师用来讲时间复杂度的例子,看起来《不难》,但是却有多种解法,下面我记录了几种比较常见的解法,供读者参考。

解法1(暴力破解)

总体思路

这题比较容易想到的方法可能就是暴力破解法(似乎大多数题目都可以用暴力破解法来解决),其思路也很简单,就是遍历出所有的可能,然后找出符合条件的即可。就本题而言,就是遍历所有的连续子数组(包括不同位置,不同长度等),依次计算出他们的和,从中找出最大的即可。

代码实现

public static int maxSubArray(int[] nums) {
         int maxValue = nums[0];//连续数组最大值
        for(int i=0;i<nums.length;i++){//遍历连续数组起始位置
            for (int j=i;j<nums.length;j++){//遍历连续数组终止位置
                //计算所得连续数组的和
                int sumValue = 0;
                for (int k=i; k<=j;k++){
                    sumValue += nums[k];
                }
                //找出最大和
                if (sumValue > maxValue){
                    maxValue = sumValue;
                }
            }
        }
        return maxValue;
    }

运行结果

运行结果

结果分析

由以上运行结果可知该种解法对于所给示例能够得到正确答案,但是很容易想到肯定会超时,因为题目要求的时间复杂度是O(n),而此种解法的时间复杂度是O(n³),显然差距过大,提交到leetcode平台上后果然不出所料,确实超时了。
提交结果才发现,好像还有测试用例不通过的,暂且不管它了。(不过leetcode也是真的狗啊,竟然给出了个这么长的数组)

解法2(小暴力)

总体思路

解法2是在解法1的基础上进行了改进。不难发现解法1在计算连续数组的和的时候进行了很多重复计算,而这些重复计算并不是必须的,完全可以采用累加的方式实现,即这个连续数组的和是在上一个连续数组和的基础上得到的,如此一来便可以去掉一层循环。

代码实现

 public static int maxSubArray(int[] nums) {
        int maxValue = nums[0];//记录连续数组最大和
        for(int i=0;i<nums.length;i++){
            //记录连续数组的和
            int sumValue = 0;
            for (int j=i;j<nums.length;j++){
                //本次和是在上一次的基础上进行得到的
                sumValue += nums[j];
                //获取最大和
                if (sumValue > maxValue){
                    maxValue = sumValue;
                }
            }
        }
        return maxValue;
    }

运行结果

运行结果

结果分析

由以上结果可知,此种解法也能够得出正确结果,虽然时间复杂度较解法1有所改进,但是还有O(n²),依然不能满足本题要求,提交到leetcode平台后结果也是如此。

解法3(分而治之)

总体思路

记得浙江大学陈越老师曾经在他的课里说过,作为一个合格的程序员,当你设计了一个算法的复杂度为O(n²)时要想想能不能将其降为O(nlogn)。受此启发我们也可以想办法将其时间复杂度降为O(nlogn),其实我接触过的时间复杂度为O(nlogn)的算法并不多,其中还有印象的应该是归并排序,,而归并排序的核心思想就是“分治”,在本题中我们依然可以借助这种“分而治之”的思想。“分治法”的主要思想就是将大问题分解为若干个小问题,然后将各个小问题各个击破,然后再将各个子问题合并即可得到原问题的解,其实这和递归的思想有些相似,因此“分治法”一般和递归是分不开的。具体到本题而言就是利用数组的“中点”将原数组分解为左右两个小数组,分别计算出左右两个子数组中的连续子数组最大和,然后将左右两个数组合并得到包含所选中点的连续数组的最大和,最后再选出左右连续数组最大和、合并后包含中点连续数组最大和三者中的最大值即可,然后分别对左右两个小数组执行以上过程即能得到正确结果。

代码实现

public static int maxSum(int[] nums,int left,int right){
        //递归返回
        if (left == right){
           return nums[left];
        }

        //得到中点值
        int center = (left + right) / 2;
        //得到左边数组中连续数组最大和
        int leftMaxSum = maxSum(nums,left,center);
        //得到右边数组中连续数组最大和
        int rightMaxSum = maxSum(nums,center+1,right);

        //计算从中点向左连续子数组最大和
        int leftBorderSum = 0,leftMaxBorderSum = nums[center];
        for (int i = center;i>=left;i--){
            leftBorderSum += nums[i];
            if (leftBorderSum > leftMaxBorderSum){
                leftMaxBorderSum = leftBorderSum;
            }
        }

        //计算从中点向右连续数组最大和
        int rightBorderSum = 0,rightMaxBorderSum = nums[center + 1];
        for (int i = center + 1;i<=right;i++){
            rightBorderSum += nums[i];
            if (rightBorderSum > rightMaxBorderSum){
                rightMaxBorderSum = rightBorderSum;
            }
        }
        
        //返回三者中最大值
        return Math.max(Math.max(leftMaxSum,rightMaxSum),leftMaxBorderSum+rightMaxBorderSum);
    }


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

运行结果

运行结果

结果分析

由以上运行结果可以知道该种解法能够得到正确结果,但是时间复杂度为O(nlogn),而题目要求的时间复杂度为O(n),猜想可能无法满足要求,但是提交到leetcode平台后发现竟然通过了测试。
提交结果
由此可见时间复杂度O(nlogn)和O(n)是非常接近的。

解法4

总体思路

解法3虽然通过了测试,但是实际上并不满足题目要求,因此此题应该还有更加快速的解法。经分析发现,直接遍历进行累加即可得到正确结果,但是需要不断调整累加和的值,在累加和为负时需要将累加和置为0(即将这一段连续数组舍弃),因为题目要求的是连续子数组最大值,不能进行跳跃,而负值对于得到连续子数组最大值是没有任何帮助的,反而会削弱最大值。基于以上思想,可以得到解法4.

代码实现

public static int maxSubArray(int[] nums) {
        //最大值和累加值
        int maxSum = nums[0],sum = 0;
        for (int i=0;i<nums.length;i++){
            //对数组进行累加
            sum += nums[i];
            //更新最大值
            if (sum > maxSum ){
                maxSum = sum;
            }
            //更新累加值
            if (sum < 0){
                sum = 0;
            }
        }
        return maxSum;
    }

运行结果

运行结果

结果分析

由以上运行结果可知解法4也能够得到正确结果,而且时间复杂度为O(n),提交到leetcode平台后也能够得到正确结果。
提交结果

总结

此题共用了4种解法进行解决,时间复杂度也不尽相同,解法1和解法2是常规解法,解法3值得认真学习,解法4具有一定的技巧,有时难以想到。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值