无论是在生活中还是在计算机科学中,“分而治之”的思想占据着举足轻重的地位,其原理如下:
1)将一个复杂的问题分成若干个相同或相似的子问题
2)递归求解子问题,当子问题规模很小时,可直接求解
3)将所有子问题的解合并,即为原问题的解
1.最大子数组问题(maximum-subarray problem)
即一段非空的,连续的有最大和的一段子数组,如:
若采用暴力求解方法,时间复杂度是Θ(n²)。这里我们用分治法求解,将数组分为两个尽可能等长的子数组,那么最大子数组A[i...j]存在三种情况:
1)完全在子数组A[low...mid]中,满足low ≤ i ≤ j ≤ mid
2)完全在子数组A[mid+1...high]中,满足 mid<i≤j≤high
3)跨越中点(midpoint),满足low ≤ i ≤ mid < j ≤ high
当为情况3时,只需要寻找形如A[i...mid]和A[mid+1...high]的最大子数组,然后连接他们。伪代码如下:
其运行时间为:O(n)。
最大子数组算法的伪代码如下:
运行时间为:
最大子数组问题也可以在线性时间内求解,具体思想参加算法导论(第三版)课后题4.1-5.
三种求解方法的完整代码如下:
#include<iostream>
#include<limits.h>
using namespace std;
int maxSubarr_start; //save the start index of maximum subarray with divid-and-conquer solution
int maxSubarr_end;
void Bruteforce_MaxSubarr(int a[],int n,int *res)
{//Brute-force solution
int maxSum=INT_MIN;
for(int i=0;i<n;i++)
for(int j=i;j<n;j++)
{
int temp=0;
for(int k=i;k<=j;k++)
temp+=a[k];
if(temp>maxSum)
{
maxSum=temp;
res[0]=i; //store the start index in array res[0]
res[1]=j; //store the end index in array res[1]
res[2]=maxSum; //store the maxSum in array res[2]
}
}
}
int Crossing_MaxSubarr(int a[],int low,int mid,int high)
{//case:maximum subarray corssing mid index
int left_sum=INT_MIN;
int sum=0;
for(int i=mid;i>=low;i--)
{
sum+=a[i];
if(sum>left_sum)
{
left_sum=sum;
maxSubarr_start=i;
}
}
int right_sum=INT_MIN;
sum=0;
for(int j=mid+1;j<=high;j++)
{
sum+=a[j];
if(sum>right_sum)
{
right_sum=sum;
maxSubarr_end=j;
}
}
return left_sum+right_sum;
}
int DC_MaxSubarr(int a[],int low,int high)
{//divide-and-conquer solution
if(high==low) //only one element
return a[low];
//divid-and-conquer
int mid=(low+high)/2;
int left_sum = DC_MaxSubarr(a,low,mid);
int right_sum = DC_MaxSubarr(a,mid+1,high);
int cross_sum = Crossing_MaxSubarr(a,low,mid,high);
if(left_sum>right_sum && left_sum>cross_sum)
return left_sum;
else if(right_sum>left_sum && right_sum>cross_sum)
return right_sum;
else
return cross_sum;
}
void LinearTime_MaxSubarr(int a[],int low,int high,int *res)
{//linear time solution
int temp=0;
int maxSum=INT_MIN;
int max_start,max_end;
for(int i=low;i<=high;i++)
{
temp+=a[i];
if(temp>maxSum) //if a[i]<=0 ,refuse
{
maxSum=temp;
max_end=i; //the end index of maximum subarray
}
if(temp<0) //start at a[i+1]
temp=0;
}
//find the start index of maximum subarray
temp=maxSum;
for(int j=max_end;j>=low;j--)
{
temp-=a[j];
if(temp==0)
{
max_start=j;
break;
}
}
//cout<<"the result:"<<max_start<<" "<<max_end<<" "<<maxSum<<endl;
res[0]=max_start;
res[1]=max_end;
res[2]=maxSum;
}
int main()
{
int a[]={13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7};
int n=sizeof(a)/sizeof(int);
/*-------the maximum-subarray with brute-force solution as following:------*/
int *result0;
result0=new int[3];
Bruteforce_MaxSubarr(a,n,result0);
cout<<"Brute-force solution as following:"<<endl;
cout<<result0[0]<<" "<<result0[1]<<" "<<result0[2]<<endl;
/*---the maximum-subarray with divid-and-conquer solution as following:----*/
int result1;
cout<<"Divid-Conquer solution as following:"<<endl;
result1=DC_MaxSubarr(a,0,n-1);
cout<<maxSubarr_start<<" "<<maxSubarr_end<<" "<<result1<<endl;
/*-------the maximum-subarry with linear-time solution as following:------*/
int *result2;
result2=new int[3];
cout<<"Linear-time solution as following :"<<endl;
LinearTime_MaxSubarr(a,0,n-1,result2);
cout<<result2[0]<<" "<<result2[1]<<" "<<result2[2]<<endl;
return 0;
}
运行结果:
第一个数表示最大子数组的起始位置;第二个是表示终止位置;第三个数表示最大子数组和。