最大子数组: 有一数组A,求出A的和最大 的子数组,称这个连续子数组称为最大子数组。
因为一个数组可能有多个子数组达到最大和,在这里是只求出一个最大子数组。
如下面的一个数组。
假定我们查找A[low...high]中的最大子数组,使用分治技术我们可以分成两个规模尽量相等的子数组,
也就是说找到子数组的中央; 即:
mid = low + (high - low) / 2;<span style="font-family: Arial, Helvetica, sans-serif;"> </span>
那么最大子数组A[i...j]可能所有三种情况
1. 完全位于左子数组 A[low...mid] ; low <=i <=j <= high;
2.完全位于右子数组 A[mid+1...high] mid<i <=j <=high;
3.跨越中点,因此 low <= i< j <= high.
经过分析, A[low...high]的一个最大子数组所处的位置,必然是上述三种情况之一。
实际上,就是三种情况下,求出的最大的那个。
通过递归求解 A[low...mid], A[]mid+1...high]中的最大子数组,因为这两个子问题仍是最大的子数组问题。
然后再求跨越中点的最大子数组,然后在这再种情况下选取最大值。
首先先完全求出跨越中点的最大子数组。
int find_max_cross_maxsum(int *a, int low, int mid, int high){
int left_sum = -65535; //存放一个数组 a 不可能出现的最小值,用于比较。
int right_sum = -65535;
int sum = 0;
int i = 0;
for(i = mid; i>=low; i--){
sum += a[i];
if( sum > left_sum ){
left_sum = sum;
}
}
sum = 0;
for(i=mid+1; i<=high; i++){
sum+=a[i];
if( sum > right_sum){
right_sum = sum;
}
}
return left_sum + right_sum;
}
递归求出最大子数组。
int find_max_subarray(int *a, int low, int high){
int left_sum, right_sum, cross_sum, mid;
if(low == high){
return a[low]; // 递归结束条件。
}
else{
mid = low + (high-low) / 2;
left_sum = find_max_subarray(a, low, mid); //求出左子数组最大子数组。
right_sum = find_max_subarray(a, mid+1, high);//求出右子数组最大子数组。
cross_sum = find_max_cross_maxsum(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;
}
}
测试代码:
int main(){
int a[] = {13, -3, -25, 20, -3, -16, -23, 18, 20, -7 , 12, -5, -22 , 15};
int max_sum = find_max_subarray(a, 0, sizeof(a)/sizeof(a[0]) - 1);
printf("%d\n", max_sum);
}
结果返回 43, 即 18, 20 ,-7 , 12 这四个连续位置的和,
如果想具体知道是那个下标到那个下标,可以自行修改。