LeetCode之Maximum Subarray

Find the contiguous subarray within an array (containing at least one number) which has the largest sum.

For example, given the array [−2,1,−3,4,−1,2,1,−5,4],
the contiguous subarray [4,−1,2,1] has the largest sum = 6.
题目描述:最大子段和问题
思路:看到这道题目最简单的思路就是对0 <= i <= j < n的整数进行迭代,对每个整数对程序都要计算x[i..j]的总和,然后检验该综合是否大于迄今为止的最大总和,算法伪码如下:

maxsofar = nums[0]
for i = [0,n)
    for j=[i,n)
        sum = 0//sum of nums[i..j]
        for k = [i,j]
            sum+=nums[k]
        maxsofar = max(maxsofar, sum)

显然该思路的时间复杂度为 O(n3) ,本题明显会超时,不是可取的算法。
那么如何降低该算法的时间复杂度呢,可以看到sum的计算涉及到了大量的重复计算而sum(nums[i..j])与sum(nums[i..j-1])之间密切相关,因此算法可以进一步改进,其伪码如下:

maxsofar = nums[0]
for i = [0,n)
    sum=0
    for j=[i,n)
        sum+=nums[j]
        maxsofar = max(maxsofar, sum)

该算法的时间复杂度为 O(n2) ,但是在leetcode测评仍然超时通不过。降低重复的计算还有一种方法,就是保存中间的计算结果,假设cumarr[i]=sum(nums[0…i]),那么sum(nums[i…j]) = cumarr[j] - cumarr[i-1],因此事先计算好cumarr[i],同样可以将算法的时间复杂度降低为 O(n2) ,其伪码如下:

cumarr[-1]=0
for i=[0,n)
    cumarr[i] = cumarr[i-1] + nums[i]
maxsofar = nums[0]
for i = [0,n)
    for j=[i,n)
        sum=cumarr[j]-cumarr[i-1]
        maxsofar = max(maxsofar, sum)

上面两种改进的算法只能将算法的时间复杂度降为 O(n2) ,那么还有时间复杂度更低的算法么?答案显然是有。可以采用分治法,将整个数组划分成左右两个部分,最大子段和要么在左边要么在右边,要么在两者中间,代码如下:

class Solution {
public:
    int maxsum(vector<int>& nums, int l, int u){
        if(l > u)
            return 0;
        if(l == u)
            return nums[l];
        int m = (l + u) / 2;
        int lmax, sum, rmax;
        lmax = nums[m];
        sum = 0;
        for(int i = m; i >= l; --i){
            sum += nums[i];
            lmax = max(lmax, sum);
        }
        rmax = nums[m+1];
        sum = 0;
        for(int i = m + 1; i <= u; ++i){
            sum += nums[i];
            rmax = max(rmax, sum);
        }
        return max(rmax+lmax, max(maxsum(nums, l, m), maxsum(nums, m+1, u)));
    }
    int maxSubArray(vector<int>& nums){
        maxsum(nums, 0, nums.size()-1);
    }
};

该代码通过了leetcode的测评,由于总共进行了 O(logn) 层递归,在每一层递归均进行了 O(n) 次操作,因此算法的时间复杂度为 O(nlogn) 。通过查阅相关资料,本题还有时间复杂度为 O(n) 的算法。算法的原理是:前i个元素中,最大子段和要么在前i-1个元素中(存储在maxsofa),要么其结束位置为i(存储在maxendinghere)。
假设我们已经解决了nums[0..i-1]的最大子段和问题,那么利用上述原理将其扩展到nums[0..i]上:
maxsofar是nums[0..i-1]的最大子段和,那么nums[0…i]的最大子段和要么为maxsofa要么为maxendinghere(结束位置为i);对于结束位置为i-1的maxendinghere,如果maxendinghere>0,那么maxendinghere=maxendinghere+nums[i],如果maxendinghere<=0,那么maxendinghere=nums[i]。
最后maxsofar = max(maxsofar, maxendinghere),即可求出nums[0…i]的最大子段和。代码如下:

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int size = nums.size();
        int maxsofar = nums[0];
        int maxendinghere = 0;
        for(int i = 0; i < size; ++i){
            maxendinghere = maxendinghere > 0 ? maxendinghere+nums[i] : nums[i];
            maxsofar = max(maxsofar, maxendinghere);
        }
        return maxsofar;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值