【leetcode记录帖】【5】Maxmum Subarray

最大子序和问题。要求连续数组和最大。

1、纯暴力,枚举区间端点,三重循环O(n^3)超出时间限制

class Solution {
    public int maxSubArray(int[] nums) {
        int maxn=Integer.MIN_VALUE;
        if(nums.length==1) return nums[0];
        int sum=0;
        for(int i=0;i<nums.length;i++)
        {
            for(int j=i;j<nums.length;j++){
                sum=0;
                for(int k=i;k<=j;++k){
                    sum+=nums[k];
                }
                if(sum>maxn) maxn=sum;
            }

        }
        return maxn;
    }
}

2、前缀和预处理 , 没想到通过了,复杂度O(n^2)数据蛮水的

class Solution {
    public int maxSubArray(int[] nums) {
        int maxn=Integer.MIN_VALUE;
        if(nums.length==1) return nums[0];
        int sum=0;
        int[] sums=new int[1000100];    
        for(int i=0;i<nums.length;i++){
            sums[i+1]=nums[i]+sums[i];
        }
        for(int i=1;i<=nums.length;i++)
        {
            for(int j=i;j<=nums.length;j++){
                if(sums[j]-sums[i-1]>maxn) maxn=sums[j]-sums[i-1];
            }
        }
        return maxn;
    }
}

不用前缀和也可以二重循环过。

class Solution {
    public int maxSubArray(int[] nums) {
        
        if(nums.length == 1) return nums[0];
        int sum;
        int maxn = Integer.MIN_VALUE;
        for(int i = 0; i < nums.length; i++)
        {
            sum = 0;
            for(int j=i;j < nums.length;++j){
                sum += nums[j];
                if(sum > maxn) maxn = sum;
            }
        }
        return maxn;
    }
}

 

3、贪心

若当前指针所指元素之前的和小于0,则丢弃之前的数组序列。

稍微想想,如果前面的和已经是负值了,那么下一个值如果是负数,只会让和更小。如果是正数,加上前面的负的和值也要小于这个数本身,还不如就从这个数开始求和,所以抛弃前面的数组序列(别忘了本题要求连续)。贪心策略:抛弃负的和值。

O(n)通过!

class Solution {
    public int maxSubArray(int[] nums) {
        
        if(nums.length == 1) return nums[0];
        int sum = 0;
        int maxn = Integer.MIN_VALUE;
        for(int i=0; i < nums.length; i++)
        {
            sum += nums[i];
            if(sum > maxn) maxn = sum;
            if(sum<0) {
                if(sum > maxn) maxn = sum;
                sum=0;
            }
        }
        return maxn;
    }
}

3、动态规划

看到写遍历的思路很好的总结 :

示例: [a, b , c, d , e]

解答这类题目, 省略不掉遍历, 因此我们先从遍历方式说起

通常我们遍历子串或者子序列有三种遍历方式

以某个节点为开头的所有子序列: 如 [a],[a, b],[ a, b, c] ... 再从以 b 为开头的子序列开始遍历 [b] [b, c]。
根据子序列的长度为标杆,如先遍历出子序列长度为 1 的子序列,在遍历出长度为 2 的 等等。
以子序列的结束节点为基准,先遍历出以某个节点为结束的所有子序列,因为每个节点都可能会是子序列的结束节点,因此要遍历下整个序列,如: 以 b 为结束点的所有子序列: [a , b] [b] 以 c 为结束点的所有子序列: [a, b, c] [b, c] [ c ]。
第一种遍历方式通常用于暴力解法, 第二种遍历方式 leetcode (5. 最长回文子串 ) 中的解法就用到了。

第三种遍历方式 因为可以产生递推关系, 采用动态规划时, 经常通过此种遍历方式, 如 背包问题, 最大公共子串 , 这里的动态规划解法也是以 先遍历出 以某个节点为结束节点的所有子序列 的思路

对于刚接触动态规划的, 我感觉熟悉第三种遍历方式是需要抓住的核心

因为我们通常的惯性思维是以子序列的开头为基准,先遍历出以 a 为开头的所有子序列,再遍历出以 b 为开头的...但是动态规划为了找到不同子序列之间的递推关系,恰恰是以子序列的结束点为基准的,这点开阔了我们的思路。

我在网上看不少解答时,直接阅读其代码,总是感觉很理解很吃力,因为好多没有写清楚,一些遍历到底代表什么意思,看了许久仍不知所以然,下面的代码中摘录了 维基中的解释,感觉比较清楚,供大家理解参考。

在每一个扫描点计算以该点数值为结束点的子数列的最大和(正数和)。


// 该子数列由两部分组成:以前一个位置为结束点的最大子数列、该位置的数值。
// 因为该算法用到了“最佳子结构”(以每个位置为终点的最大子数列都是基于其前一位置的最大子数列计算得出, 
// 该算法可看成动态规划的一个例子。
// 状态转移方程:sum[i] = max{sum[i-1]+a[i],a[i]}   
// 其中(sum[i]记录以a[i]为子序列末端的最大序子列连续和)

作者:lao-hu-8
链接:https://leetcode-cn.com/problems/maximum-subarray/solution/xiang-xi-jie-du-dong-tai-gui-hua-de-shi-xian-yi-li/
来源:力扣(LeetCode)

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

用dp[i]表示以nums[i]为最后一个数的最大值,体现动态规划的地方:某一个最优结构是由前面的最优子结构所决定的。

我的代码(c++实现):

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        
        int maxn = INT_MIN;
        int le = nums.size();
        vector<int> dp(le);
        dp[0] = nums[0];
        maxn=dp[0];
        for(int i = 1;i < le; ++i){
            dp[i]=max(dp[i-1]+nums[i],nums[i]);
            maxn=max(dp[i],maxn);
        }
        return maxn;
    }
};

 

 

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值