这里记录的是从《算法导论》看来的解法,待补充。
【解法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 ̄)╭♡