再解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.

以前做过这个题,最近在复习分治法,总结了这个题的三种方法发了一个post:

OK...this is a well-known divide and conquer problem, however, before we start to talk about divide and conquer, let's first see another two methods:
1) Brute-force
The most naive method, but easy to understand:

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int len=nums.size();
        int sum=0;
        vector<int> sum_per_round;
        vector<int> max_per_round;
        for(int i=0;i<len;i++){
            sum=nums[i];
            sum_per_round.push_back(sum);
            for(int j=i+1;j<len;j++){
                sum+=nums[j];
                sum_per_round.push_back(sum);
            }
            max_per_round.push_back(*max_element(sum_per_round.begin(),sum_per_round.end()));
            sum_per_round.clear();
        }
        return *max_element(max_per_round.begin(),max_per_round.end());
    }
};

Interestingly, I passed 201/202 cases using these funny codes... but got a sad TLE :-)
It's not hard to tell that the time complexity of this solution is O(n^2).

2)Dynamic Programming
In dynamic programming, the key is to find the dynamic programming equation. To get this equation, we can first study some examples, say, nums[3]={-2,3,3}. In position0, the maximum sub-array is nums[0], i.e. -2. Then what about the maximum sub-array ending with nums[1]? Should it be -2+3 or simply 3? Obviously, it should be 3 because the maximum sub-array ending with nums[0] is less than 0. Similarly, consider the maximum sub-array ending with nums[2], this time we should add 3 to nums[2] because the maximum sub-array ending with nums[1] is greater than 0. Now we see the pattern here:
Suppose DP[i] is the maximum sub-array ending with nums[i]. If DP[i-1]>0,DP[i]=A[i]+DP[i-1],otherwise DP[i]=A[i].
Based on this equation, we have the following codes:

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int len=nums.size();
        int* dp=new int[len];
        int res=dp[0]=nums[0];
        for(int i=1;i<len;i++){
            dp[i]=nums[i]+(dp[i-1]>0?dp[i-1]:0);
            res=max(res,dp[i]);
        }
        delete dp;
        return res;
    }
};

The time complexity is O(n), much faster than Brute-force.

3) Divide and Conquer
Divide and Conquer usually needs three steps:
a. Divide
Divide a big problem into sub-problems with smaller size. Traditionally, we usually divide by a half :-)
b. Conquer
Recursively solve each sub-problem.
c. Combine
Combine the answer we get in Conquer step to form the complete answer.
In maximum sub-array problem, consider the input array nums[right,left], the maximum sub-array ans[i,j]:
①may completely locate in the left part, i.e. nums[left,mid], if left<=i<=j<=mid, where mid=(left+right)/2
②may completely locate in the right part, i.e. nums[mid+1,right], if left<=i<=mid<j<=right, where mid=(left+right)/2
③may cover the mid, i.e. mid∈[i,j], if left<=i<=mid<j<=right

We can recursively compute the maximum sub-array of nums[left,mid] and nums[mid+1,right], also, we need a function to compute the maximum sub-array when ans[i,j] covers the mid.
The following codes are from the famous book CLRS( so... it's not my original work, thanks to this great book :-)

class Solution {
public:
    int maxSubArray(vector<int>& nums){
	    int len=nums.size();
	    if(len==0)// empty input
		    return 0;
            else
		    return find_maximum_subarray(nums,0,len-1);
    }
    
    // the routine finds the max subarr in the left half and right half
    int find_maximum_subarray(vector<int>& nums,int low,int high){
	    if(low==high)// base case
		    return nums[low];
	    else{
		    int mid=(low+high)/2;// divide
                    // conquer
		    int left_sum=find_maximum_subarray(nums,low,mid);
		    int right_sum=find_maximum_subarray(nums,mid+1,high);
		    int cross_sum=find_max_crossing_subarray(nums,low,mid,high);
		    // combine and return
                    return max(max(left_sum,right_sum),cross_sum);
	    }
    }
    
    // the routine finds the max subarr that crosses mid
    int find_max_crossing_subarray(vector<int>& nums,int low,int mid,int high){
	    int left_sum=INT_MIN;
	    int right_sum=INT_MIN;
    	    int sum=0;
            // compute the largest sum from low to mid
	    for(int i=mid;i>=low;i--){
		    sum+=nums[i];
		    if(sum>left_sum)
			    left_sum=sum;
	    }
	    sum=0;// reset sum
            // compute the largest sum from mid+1 to high
	    for(int i=mid+1;i<=high;i++){
		    sum+=nums[i];
		    if(sum>right_sum)
			    right_sum=sum;
        }
	    return left_sum+right_sum;
    }
};

Now let's briefly analyze this method's time complexity, the recursion of the method is T(n)=2T(n/2)+Θ(n), by applying the Master Thereom, we know that the time complexity is O(nlgn).
For more details, please refer to CLRS.





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值