最大子数组是数组A的和最大的非空连续子数组。只有当数组中包含负数时,最大子数组问题才有意义。注意将实际问题转化为数学问题!
使用分治策略的求解方法:
为寻找A[low..high]的最大子数组,其中央位置记为mid,然后考虑求解两个子数组A[low..mid]和A[mid+1..high]。A[low..high]的任何子数组A[i..j]所处的位置必然是以下三种情况之一:
1. 完全位于子数组A[low..mid]中,因此low <= i<= j <=mid。
2. 完全位于子数组A[mid+1..high]中,因此mid < i<= j <= high。
3. 跨越了中点,因此low <= i <= mid < j <= high。
实际上,A[low..high]的最大子数组必然是以上三种情况下所有子数组中的最大者。
任何跨越中点的子数组都由两个子数组A[i..mid]和A[mid+1..j]组成,只需分别找出A[i..mid]和A[mid+1..j]的最大子数组,然后将其合并即可。
以下伪代码接收数组A和下标low,mid和high作为输入,返回一个下标元组划定跨越中点的最大子数组的边界,并返回最大子数组中值的和。
FIND-MAX-CROSSING-SUBARRAY(A, low, mid,high)
left-sum = - //保存目前为止最大的和
sum = 0 //保存A[i..mid]中所有值的和
for i = mid downto low
sum = sum + A[i]
ifsum > left-sum //每当找到一个子数组A[i..mid]的和大于left-sum
left-sum = sum //将left-sum更新为该子数组的和
max-left = i //更新变量max-left来记录当前下标i
right-sum = -
sum = 0
for j = mid + 1 to high
sum = sum + A[j]
ifsum > right-sum
right-sum = sum
max-right = j
return (max-left, max-right, left-sum +right-sum)
求解最大子数组问题的分治算法的伪代码如下:
FIND-MAXINUM-SUBARRAY(A, low, high)
if high == low //子数组只有一个元素的情况
return(low,high,A[low])
else mid = //划分子数组,计算中点下标mid
//递归求解左右子数组中的最大子数组
(left-low,left-high,left-sum)= FIND-MAXINUM-SUBARRAY(A, low, mid)
(right-low,right-high,right-sum)= FIND-MAXINUM-SUBARRAY(A, mid+1, high)
//求跨越中点的最大子数组
(cross-low,cross-high,cross-sum)= FIND-MAX-CROSSING-SUBARRAY(A,low,mid,high)
// 检测最大子数组位于左子数组,右子数组还是跨越中点的数组
ifleft-sum >= right-sum and left-sum >= cross-sum
return(left-low,left-high,left-sum)
elseifright-sum >= left-sum and right-sum >= cross-sum
return(right-low,right-high,right-sum)
elsereturn(cross-low,cross-right,cross-sum)
对应的C++程序代码如下:
#include <iostream>
int Find_MAX_CROSSING_SUBARRAY(int *A,intlow,int mid,int high,int& max_left,int& max_right,int& max_value)
{
intleft_sum= -1000000;//不可能的最小值
intsum = 0;
for(inti= mid;i>low;i--)
{
sum= sum+A[i];
if(sum>left_sum)
{
left_sum= sum;
max_left=i;
}
}
intright_sum = -1000000;//不可能的最小值
sum=0;
for(intj=mid+1;j<high;j++)
{
sum= sum+A[j];
if(sum>right_sum)
{
right_sum= sum;
max_right=j;
}
}
max_value= left_sum+right_sum;
return0;
}
int Find_MaxiMum_SubArray(int *A,intlow,int high,int& max_left,int& max_right,int& max_value)
{
if(high==low)
{
max_left = low;
max_right= high;
max_value= A[low];
}
else
{
intmid=(low+high)/2;
inttmp_left_low;
inttmp_left_high;
inttmp_left_sum;
Find_MaxiMum_SubArray(A,low,mid,tmp_left_low,tmp_left_high,tmp_left_sum);
inttmp_right_low;
inttmp_right_high;
inttmp_right_sum;
Find_MaxiMum_SubArray(A,mid+1,high,tmp_right_low,tmp_right_high,tmp_right_sum);
inttmp_cross_low;
inttmp_cross_high;
inttmp_cross_sum;
Find_MAX_CROSSING_SUBARRAY(A,low,mid,high,tmp_cross_low,tmp_cross_high,tmp_cross_sum);
if((tmp_left_sum>=tmp_right_sum)&&(tmp_left_sum>=tmp_cross_sum))
{
max_left = tmp_left_low;
max_right = tmp_left_high;
max_value = tmp_left_sum;
}
elseif((tmp_right_sum>=tmp_left_sum)&&(tmp_right_sum>=tmp_cross_sum))
{
max_left = tmp_right_low;
max_right = tmp_right_high;
max_value = tmp_right_sum;
}
else
{
max_left = tmp_cross_low;
max_right = tmp_cross_high;
max_value = tmp_cross_sum;
}
}
return0;
}
int B[16] ={13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7};
int main()
{
std::cout<<std::endl;
intmax_left,max_right,max_value;
Find_MaxiMum_SubArray(B,0,16,max_left,max_right,max_value);
std::cout<<max_left<<"\t"<<max_right<<"\t"<<max_value<<std::endl;
std::cout<<std::endl;
system("pause");
return0;
}