编程之美2.14——求和最大子数组

这里记录的是从《算法导论》看来的解法,待补充。

【解法1】——递归,二分

将数组分成左右两边,最大子数组出现的情况可能有:

1)左半边

2)右半边

3)跨左右两边

对于半边求最大子数组,又可以递归上述思维,所以难点在于写出跨左右两边的情况。

代码:

void find_max_cross_subarray(int src[], int low, int mid, int high, 
    int &left_max, int &right_max, int &subsum)
{
    int sum=0;
    int left_sum=INT_MIN;
    for(int i=mid; i>=low; i--){
        sum+=src[i];
        if(sum>left_sum){
            left_sum=sum;
            left_max=i;
        }
    }
    sum=0;
    int right_sum=INT_MIN;
    for(int j=mid+1; j<=high; j++)
    {
        sum+=src[j];
        if(sum>right_sum){
            right_sum=sum;
            right_max=j;
        }
    }
    subsum=left_sum+right_sum;
}
void find_max_array(int src[], int low, int high, int &left_max, int& right_max, int& subsum)
{
    if(low==high){
        left_max=low;
        right_max=high;
        subsum=src[low];
        return;
    }        
    int mid=(low+high)/2;
    int left_low, left_high, left_sum;
    int right_low, right_high, right_sum;
    int cross_low, cross_high, cross_sum;
    find_max_array(src, low, mid, left_low, left_high, left_sum);
    find_max_array(src, mid+1, high, right_low, right_high, right_sum);
    find_max_cross_subarray(src, low, mid, high, cross_low, cross_high, cross_sum);
    if(left_sum>=right_sum&&left_sum>=cross_sum){
        left_max=left_low;
        right_max=left_high;
        subsum=left_sum;

    }
    else if(right_sum>=left_sum&&right_sum>=cross_sum){
        left_max=right_low;
        right_max=right_high;
        subsum=right_sum;
    }
    else{
        left_max=cross_low;
        right_max=cross_high;
        subsum=cross_sum;
    }
    return;
}

评价:

1)跨两边的情况,左边要从中间开始逆推,右边从中间开始顺推。

2)每一趟遍历得到一个从当前位置开始的最大子数组,记录最大和,如果加上s[next]之后大于最大和,则更新,并记录next。

 

【未成熟待验证方案】

从sum=s[0],sum<0时,sum=s[next],即从下一个节点开始重新记录最大和,然后像上面第2点技巧遍历。

当数组全是负数时,就找出全员最大值返回。

int Maxsum_ultimate(int * arr, int size)
{
    int maxSum = -INF;
    int sum = 0;
    for(int i = 0; i < size; ++i)
    {
        if(sum < 0)
        {
            sum = arr[i];
        }else
        {
            sum += arr[i];
        }
        if(sum > maxSum)
        {
            maxSum = sum;
        }
    }
    return maxSum;
}

评价:多么简单呀!参考博文:http://www.ahathinking.com/archives/120.html

【解法2】——动态规划

把问题分解为一个n-1规模的数组和一个当前考量元素。如考量第一个元素A[0],以及最大的一段数组(A[i]...A[j])和A[0]之间的关系,有下列几种情况:

1.当0=i=j时,元素A[0]本身构成和最大的一段;

2.当0=i<j时,和最大的一段以A[0]开始;

3.当0<i时,元素A[0]跟和最大的一段没有关系。

用start[i]记录包含A[i]的和最大数组的和,all[i]为从A[i]-A[N-1]和最大一段数组的和,从后向前遍历,就把时间缩到了O(n)!

int find_max_array1(int n[], int length)
{
    int *start=new int[length];
    int *all=new int[length];
    start[length-1]=n[length-1];
    all[length-1]=n[length-1];
    for(int i=length-2; i>=0; i--){
        start[i]=max(n[i], n[i]+start[i+1]);
        all[i]=max(start[i], all[i+1]);
    }
    delete [] start;
    delete [] all;
    return all[0];
}

还有改进,把空间也降为O(1)。

int find_max_array1(int n[], int length)
{
    nstart=n[length-1];
    nall=n[length-1];
    for(int i=length-2; i>=0; i--){
        nstart=max(n[i], n[i]+nstart);
        nall=max(start[i], nall);
    }
    return nall;
}

评价:这个简短到简直不敢想(╭ ̄3 ̄)╭♡

 

posted on 2015-04-29 08:32  EmmaLi  阅读(137)  评论(0编辑  收藏  举报

导航