53.Maximum Subarray

三种方法:

1.暴力遍历:两层循环,不必多说O(N^2)

2.分治法:O(N*logN)

思路:最大和子串要么出现在左半部分,要么出现在右半部分,要么出现在跨越中间的部分。于是有下面的分治法。

class Solution {
public:
    int solve(vector<int>&nums,int low,int high){
        if(low==high) return nums[low];
        int mid=(low+high)/2;

        //找出跨越中间的左半部分的最大值
        int max_left=nums[mid];
        int sum=0;
        for(int i=mid;i>=low;i--){
            sum+=nums[i];
            if(sum>max_left){
                max_left=sum;
            }
        }

        //找出跨越中间的右半部分的最大值
        int max_right=nums[mid+1];
        sum=0;
        for(int i=mid+1;i<=high;i++){
            sum+=nums[i];
            if(sum>max_right){
                max_right=sum;
            }
        }        

        //这里即是跨越中间的最大和部分
        int max_sum=max_left+max_right;

        //比较左半部分,右半部分,跨越中间的部分,取最大值即所求。
        return max(max_sum,max(solve(nums,low,mid),solve(nums,mid+1,high)));
    }
    int maxSubArray(vector<int>& nums) {
        int len=nums.size();
        if(len==0) return 0;
        return solve(nums,0,len-1);
    }
};

3.动态规划:O(N)

思路:现在对线性时间解法做一下解释,属于一种DP问题。已知了前k个元素的最大子序列和为maxSub(已经被记录下来了),以及一个临时和sum,如果添加了第k+1这个元素,由于是连续子序列这个限制,所以如果k+1这个元素之前的和是小于0的,那么对于增大k+1这个元素从而去组成最大子序列是没有贡献的,所以可以把sum 置0。举个例子,-1, -2 ,4, -5, 7这里假定7为第k+1个元素,那么很明显可以看出,之前的sum = -5 + 4 =-1,那么这样对于7来说只会减少它,所以直接置sum = 0, 0 + 7才能得到正确的答案。再拓展这个数组, -1, -2, 4, -5, 7, 1 这里1之前的sum = 7 > 0,对于后面的1来组成最大子序列是有贡献的,所以sum = 7 + 1 =8。再注意一点,只要sum不减到负数,中间出现小于0的元素是没关系的,sum仍然可以继续累加。

证明:理解一下两点就就行。

1.首先,对于array[1...n],如果array[i...j]就是满足和最大的子串,那么对于任何k(i<=k<=j),我们有array[i...k]的和大于0。因为如果存在k使得array[i...k]的和小于0,那么我们就有array[k+1...j]的和大于array[i...j],这与我们假设的array[i...j]就是array中和最大子串矛盾。

2.其次,我们可以将数组从左到右分割为若干子串,使得除了最后一个子串之外,其余子串的各元素之和小于0,且对于所有子串array[i...j]和任意k(i<=k<j),有array[i...k]的和大于0。此时我们要说明的是,满足条件的和最大子串,只能是上述某个子串的前缀,而不可能跨越多个子串。我们假设array[p...q],是array的和最大子串,且array[p...q],跨越了array[i...j],array[j+1...k]。根据我们的分组方式,存在i<=m<j使得array[i...m]的和是array[i...j]中的最大值,存在j+1<=n<k使得array[j+1...n]的和是array[j+1...k]的最大值。由于array[m+1...j]使得array[i...j]的和小于0。此时我们可以比较array[i...m]和array[j+1...n],如果array[i...m]的和大于array[j+1...n]则array[i...m]>array[p...q],否array[j+1...n]>array[p...q],无论谁大,我们都可以找到比array[p...q]和更大的子串,这与我们的假设矛盾,所以满足条件的array[p...q]不可能跨越两个子串。

总结:根据上述结论,我们就得到了Kadane算法的执行流程,从头到尾遍历目标数组,将数组分割为满足上述条件的子串,同时得到各子串的最大前缀和,然后比较各子串的最大前缀和,得到最终答案。我们以array={−2, 1, −3, 4, −1, 2, 1, −5, 4}为例,来简单说明一下算法步骤。通过遍历,可以将数组分割为如下3个子串(-2),(1,-3),(4,-1,2,1,-5,4),这里对于(-2)这样的情况,单独分为一组。各子串的最大前缀和为-2,1,6,所以目标串的最大子串和为6。

参考:http://blog.csdn.net/joylnwang/article/details/6859677


public:
    int maxSubArray(vector<int>& nums) {
        int len=nums.size();
        if(len==0) return 0;
        int sum=0;
        int max_value=nums[0];
        for(int i=0;i<len;++i){
            sum+=nums[i];
            if(sum>max_value){
                max_value=sum;
            }
            if(sum<0){
                sum=0;
            }
        }
        return max_value;
    }
};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值