leetcode 53. 最大子序和

26 篇文章 0 订阅
9 篇文章 0 订阅

问题描述:

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例:

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

进阶:   如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。

 

共用了三种方法:

第一种: 暴力求解法 时间复杂度  < O(n^2)

class Solution {
    public int maxSubArray(int[] nums) {
         // 先用暴力法求解
        int max = nums[0];  // 默认第一个最大

        for(int i = 0; i < nums.length; i++){
            int sum = 0;
            
            for(int j = i; j < nums.length; j++){
                sum += nums[j];
                max = max>=sum?max:sum;
            }
        }
        return max;
    }
}

 大约耗费资源情况:

109 ms41.5 MB

思路:

        开始默认最大值maxVal 是数组第一个数,然后从数组 下标为 0 开始,求 0 到 nums.length -1 序列的和,与当前最大值比较,处理是否需要更新最大值。 接下来 再从 下标 1 开始, 求 1 到 nums.length -1 序列的和, 与当前最大值比较,处理是否需要更新最大值。然后,做同样的处理,直至 下标 到 nums.length - 1 的和。 此时获得的最大值就是要求的最大连续子序列和。

第二种:动态规划思想  

class Solution {
    public int maxSubArray(int[] nums) {
         if(nums.length == 0){
            return 0;
        }
        if(nums.length == 1){
            return nums[0];
        }
        // 非循环
        int maxVal = nums[0];
        int subSumMax = 0;
        for(int x = 0; x < nums.length; x++){
            subSumMax += nums[x];
            subSumMax = subSumMax > nums[x]?subSumMax:nums[x];
            maxVal = maxVal>subSumMax?maxVal:subSumMax;
            // System.out.println(maxVal);
        }
        return maxVal;
    }
}

使用资源情况:

2 ms38.7 MB

思路:(最优方法)

subSumMax表示当前累加和最大值, maxVal表示 全局最大值。

首先 subSumMax 加上当前nums[x] , 然后加和结果 subSumMax与nums[x]进行比较取最大值者 给subSumMax:

            subSumMax += nums[x];
            subSumMax = subSumMax > nums[x]?subSumMax:nums[x];
 

其实上面就是判断, 目前的累加和是否比当前下标索引所在的处的值nums[x]相比是否更大,不然用当前值nums[x]更新他。

最后再拿当前累加和最大值与全局最大值进maxVal行比较,如果subSumMax更大则更新最大值maxVal。

             maxVal = maxVal>subSumMax?maxVal:subSumMax;

重复进行直至到数组终止。

第三种方法:动态规划 递归实现(不推荐)

class Solution {
    public int maxSubArray(int[] nums) {
        int maxVal = nums[0];
        int subSumMax = 0;
        return getMaxVal(nums, subSumMax, maxVal);
    }

    public int getMaxVal(int []nums, int subSumMax,int maxVal){
        if(nums.length == 0){
            return 0;
        }
        if(nums.length == 1){
            return nums[0];
        }
        int i = nums.length - 1;
        // maxVal = nums[i];
        int []tmpNums = new int[i];
        for(int x = 0; x < nums.length -1; x++){
            tmpNums[x] = nums[x];
        }
        subSumMax += nums[i];
        subSumMax = subSumMax>nums[i]?subSumMax:nums[i];
        int getMax1 = getMaxVal(tmpNums, subSumMax, maxVal);
      
        if(nums.length <=2 && getMax1>0){
            subSumMax += getMax1;
            subSumMax = subSumMax>getMax1?subSumMax:getMax1;
        }
        maxVal = maxVal >getMax1?maxVal:getMax1;
        maxVal = maxVal>subSumMax?maxVal:subSumMax;

        return maxVal;
        
    }

}

大约使用资源情况:

243 ms651 MB

思路:【上面的三目运算可以自己编写max函数 】

假设 i 为数组最后数字的下标, 将nums[i]    与 nums[0 ... i-1] 看成两个部分, 假设 getMax(nums[0 .. i-1]) 是求得左边部分的和,现在只需要比较 目前 三个 数的和, 即 nums[i]  与 nums[i] + nums[i-1]  以及 getMax(nums[0 .. i-1])的最大值。

上面流程是:

首先是确定序列分成两部分 后     右边的数的和 是否大于 累加之前, 

即    subSumMax += nums[i];
        subSumMax = subSumMax>nums[i]?subSumMax:nums[i];

确定二者之间的最大值。

然后再获取左边部分的 最大值:int getMax1 = getMaxVal(tmpNums, subSumMax, maxVal);

【先略过 <= 2 if语句】

最大值初始是 数组中下标最大的数。

当前最大值 与 左边部分的最大值 进行 比较 更新最大值 :

        maxVal = maxVal >getMax1?maxVal:getMax1;

然后 与左边比较后, 还要与右边进行比较更新最大值: 
        maxVal = maxVal>subSumMax?maxVal:subSumMax;

最后 if 语句判断 <=2 的情况  

         判断 subSumMax加上getMax1之后和 getMax1的大小,并取最大值。

之所以不推荐这种解法,是因为会爆内存。我提交了五次 也就只通过了两次,其余均超出时间限制。 这只是我个人的一点点偏执,想看看递归的解法,另外上面的递归法还可以进一步优化,不过我应该不会再优化了。

参考:最大连续子序列-分治思想

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值