LeetCode 53. Maximum Subarray

题目

Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.

Example:

Input: [-2,1,-3,4,-1,2,1,-5,4],
Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6.

Follow up:

If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle.


这道题可是两年前在水算法作业的时候做过的题,翻了以前的博客发现以前不会做,巧了,现在也不会做。题目一看就想到了动态规划,可是却没想出来到底怎么划分子问题,看了以前的记录才想到解法,然后瞬间就写出了代码(并发现之前那个代码绝壁不是我写的以前的我怎么这么水啊)。

这道题的思路其实很简单,题目要求找出数组中连续的几个数字之和的最大。由于数组是连续的,因此我们以"以nums[i]结尾的数组"为基准来划分子问题,把它表示为sum[i],那么sum[i]只有两种情况,要么是nums[i]自己白手起家,要么是在它前面的sum[i - 1]的基础之上锦上添花,因此这个问题就变成了sum[i] = max(sum[i - 1] + nums[i], nums[i])。

我自己写的代码如下,时间复杂度O(n),运行时间12ms,只打败了5%,看了下以前抄的代码发现可以用max()来简化那些if们:

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int max = nums[0];
        int sum = nums[0];
        for (int i = 1; i < nums.size(); i++) {
            if (sum + nums[i] < nums[i]) {
                sum = nums[i];
            }
            else {
                sum += nums[i];
            }
            
            if (sum > max) {
                max = sum;
            }
        }
        return max;
    }
};

使用max()以后代码看起来清晰整洁的版本:

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        
        int currentSum =nums[0], totalSum = nums[0];
        
        for(int i=1; i<nums.size(); i++) {
            
            //Current max sum is either the current element OR current element + Previous Maximum subarray)
            currentSum = max(nums[i], currentSum+nums[i]); 
            
            //If the current maximum array sum is greater than the global total. Update it
            totalSum = max(totalSum, currentSum);
    }
        return totalSum;
    }
};

2020.10.1 Java:依旧差点没想到dp表达式,卑微。

Runtime: 1 ms, faster than 68.70% of Java online submissions for Maximum Subarray.

Memory Usage: 38.9 MB, less than 96.46% of Java online submissions for Maximum Subarray.

class Solution {
    public int maxSubArray(int[] nums) {
        int[] dp = new int[nums.length];
        dp[0] = nums[0];
        int max = dp[0];
        for (int i = 1; i < nums.length; i++) {
            dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);
            if (dp[i] > max) {
                max = dp[i];
            }
        }
        return max;
    }
}

 

还有另一种和这个思路很相似的解法,设置一个currentSum和一个totalSum,分别存储截至当前nums[i]的sum和全局的最大值,代码如下:

int maxSubArray(vector<int>& nums) {
        
        int currentSum = 0, totalSum = INT_MIN;
        
        for(int i=0; i<nums.size(); i++) {
            
            //Sum till this point ======= Current Sum till this point + this element
            currentSum = currentSum + nums[i]; 
            
            //If the current maximum array sum is greater than the global total. Update it
            totalSum = max(totalSum, currentSum);
            
            //If you get current as less thn 0 then its no point in carrying forward. Make it 0
            currentSum = max(0,currentSum);
    }
        return totalSum;
    }

感觉这种解法的第一句和第三句的合体就是DP解法的第一句,假设当前遇到的nums[i]会使totalSum减小,那么currentSum将保存当前的结果,并在nums[i + 1]的时候加入nums[i + 1]与totalSum比较。(理解的感觉不是特别到位,强行解释了一番,因为这个方法让我自己想我是想不到的,只能在了解方法以后自己在纸上模拟一遍)以上两个代码均来自于https://leetcode.com/problems/maximum-subarray/discuss/118509/C++-Kadane's-Algorithm,总结的很好

然后看到题目底下还有个follow up,说是可以用分治算法来解决,看了下以前的记录发现是没有的,想了一会儿还是想不到分治的解法。看了discussion里大佬的解法,大概就是,把原始的数组分成左右两半分别递归,最后要找的max的可能性有三种,要么完全是在已经分成的左右两边的一边中,要么就可能是横跨左右两边并包含中间的元素。然而就算是知道了这种解法但依旧不会写代码orz 真实的递归无能了。基本上抄着大佬的代码打出了如下代码并加了点自己的注释,运行时间8ms,击败了35%的C++ submission:

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        return maxSub(nums, 0, nums.size() - 1);
    }
    
    int maxSub(vector<int>& nums, int l, int r) {
        if (l > r) {
            return INT_MIN;
        }
        
        // recursion for left and right part
        int m = (l + r) / 2;
        int lmax = maxSub(nums, l, m - 1);
        int rmax = maxSub(nums, m + 1, r);
        
        // find out the max closest to the middle
        int ml = 0, mr = 0;
        int sum = 0;
        for (int i = m - 1; i >= l; i--) {
            sum += nums[i];
            ml = max(ml, sum);
        }
        sum = 0;
        for (int i = m + 1; i <= r; i++) {
            sum += nums[i];
            mr = max(mr, sum);
        }
        
        // return either the max in the left/right, or combines both side and the middle
        return max(max(lmax, rmax), ml + mr + nums[m]);
        
    }
};

这份代码的参考来源:https://leetcode.com/problems/maximum-subarray/discuss/20452/C%2B%2B-DP-and-Divide-and-Conquer

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值