之前在《编程之美》上看到过这道题,可以使用动态规划在O(n)时间内解出。
这篇日记给出一个性能略低,时间复杂度略高(nlogn),代码略麻烦的分治算法。(因为之前没见过这个算法,只看过动态规划和暴力求解,所以昨天晚上看到后就实现了下,还是推荐大家使用动态规划)
问题描述:在一个数组a[0,,,,n]中,找出一段连续子数组a(n1,n2,,,nk),使得这段连续子数组和最大。
分治法:求一个数组a[low,high]的最大子数组,只有3种情况:
1 该子数组位于a[low,high]的左边,即位于a[low,mid]中,则可以递归的求解a[low,mid].
2 该子数组位于a[low,high]的右边,即位于a[mid+1,high]中,则可以递归的求解a[mid+1,high].
3 该子数组位于a[low,high]的中间,即包括了a(mid),则直接从中间求解中间向左右延伸的最大子数组和,详见getMiddle函数。
4 返回上面三种情况的最大值,则是我们要求解的答案。
代码:
- int function(int a[], int low, int high);
- int getMiddle(int a[],int low,int high);
-
- int _tmain(int argc, _TCHAR* argv[])
- {
- //函数调用中,传入的参数low和high假设都是可以下标访问的
- //即最外层low=0 high=n-1
- int a[10]={-1,2,9,1,-13,-12,-14,1,10,-4};
- printf("%d",function(a,0,9));
- return 0;
- }
-
- int function(int a[], int low, int high)
- {
- if(low==high)
- return a[low];
- else
- {
- int mid=(low+high)/2;
- int left=function(a,0,mid);//最大和在左边,递归调用
- int right=function(a,mid+1,high);//最大和在右边,递归调用
- int middle=getMiddle(a,low,high);//最大和在中间的左右连续一段,调用getMiddle()
- if(middle>left && middle>right)
- return middle;
- else if(left>middle && left>right)
- return left;
- else
- return right;
- }
- }
- int getMiddle(int a[],int low,int high)
- {
- int mid=(low+high)/2;
- int left_max=-999;
- int right_max=-999;
- int sum=0;
- for(int i=mid;i>=low;i--)//计算左边的最大和
- {
- sum+=a[i];
- if(sum>left_max)
- left_max=sum;
- }
- sum=0;
- for(int i=mid+1;i<=high;i++)//计算右边的最大和
- {
- sum+=a[i];
- if(sum>right_max)
- right_max=sum;
- }
- return right_max+left_max;
- }