/*
问题: 已知数组 A = {2,4,-1,-5,3,-4,2,-6,4,1,2,2,4,-2,2};
求: 最小子数组和
分析:
一: 分治法:把问题分解成若干个简单易解的小问题,最后再把小问题归并起来,找到我们原来要解决问题的解
本问题中,我们把问题分解成两个规模尽量相等的子问题。 A[low...mid] 和 A[mid+1...high]
那最小的子数组和长啥样呢?
1: 子数组完全位于左子数组中 A[low...mid]
2: 子数组完全位于右子数组中 A[mid+1...high]
3: 子数组介于左子数组与右子数组之间
前两种情况我们有可以归到原来的问题上,对他们再次进行分解
而最后一种就需要我们做出特殊处理
(伪代码摘自 《算法导论》)
这个是求解 情况3 的算法
Find_Max_Crossiong_Subarray(A,low,mid,high)
left_sum = -无穷 //左数组最大和
sum = 0 // 临时变量
for i = mid downto low // 第一个 for 循环找出左子数组中最大子数组和
sum = sum + A[i]
if sum > left_sum
left_sum = sum
max_left = i;
right_sum = -无穷
sum = 0;
for j = mid+1 to high // 第二个 for 循环找出右子数组中最大子数组和
sum = sum + A[j]
if sum > right_sum
right_sum = sum
max_right = j;
return (max_left, max_right, left_sum + right_sum);
if high == low
return (low, high, A[low])
else mid = (low+high)/2
(left_low,left_high,left_sum) = Find_Maximun_subarray(A, low, mid)
(right_low,right_high,tight_sum) = Find_Maximun_subarray(A, mid+1, high)
(cross_low, cross_high, cross_sum) = Find_Maximun_subarray(A,low,mid,high)
if left_sum >= right_sum and left_sum >= cross_sum
return (left_low, left_high, left_sum)
else if right_sum >= left_sum and right_sum >= cross_sum
return (right_low, right_high, right_sum)
else
return (cross_low, cross_high, cross_sum)
二: 扫描法
此种方法是这类问题的最优解法。时间复杂度是 O(n)。 上面的分治算法时间复杂度是 O(nlogn)
算法实现:
int FindMaxinumSubarray1(int * A, int low, int high)
{
int Sum = 0;
int temp = 0;
for (int i = low; i <= high; i++)
{
temp += A[i];
if (temp > Sum)
Sum = temp;
if (temp < 0)
temp = 0;
}
问题就是该怎么来理解这个分段:
例如: 一开始,我们另 Sum为 负无穷,这样我们可以保证至少有一个分段的总和是大于 Sum ,并会把分段中的结果temp赋值给Sum。
如 下标i=0 时, temp += A[0] , 即 temp = 2。
ps: temp 每一次更新值,都要做两次判断
1: 判断 是否要更新 Sum 的值,即子数组最大和的值
2: 判断 是否要更新 temp的值。 每当 temp 的值被累计到 负数 时, 就更新temp的值,令 temp = 0
temp 大于 此时的 Sum , 即 Sum 被更新, Sum = temp, Sum = 2 (当前最大子数组和)
temp 大于0, 继续累加
i = 1 , temp += A[1] , 即 temp = 6
temp 大于 Sum , 更新 Sum, Sum = temp , Sum = 6 (当前最大子数组和)
temp 大于 0 , temp 继续累加
.........
当 i = 5 时, A[i] = -4
此时 temp = -1 小于 0 , 即 temp 要被更新 , temp = 0
........
ps : 其他解决这类问题的算法还有 动态规划法和暴力破解法。
下面是C语言代码实现
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
int* FindMaxCrossingSubarray(int* A, int low, int mid, int high); //寻找跨越中点的最大子数组
int* FindMaximunSubarray(int*A , int low, int high); // 寻找没有跨越中点的最大子数组
void Print(int *A,int length); //打印结果
int FindMaxinumSubarray1(int*A, int low, int high);
int main()
{
int A[] = {2,4,-1,-5,3,-4,2,-6,4,1,2,2,4,-2,2};
int *p = A;
Print(p,15);
p = FindMaximunSubarray(A,0,14);
for (int i = 0; i < 3; i++)
{
printf("%d ",*(p+i));
}
printf("-----------------------------\n");
printf("%d ",FindMaxinumSubarray1(A,0,14));
system("pause");
return 0;
}
int * FindMaxCrossingSubarray(int * A, int low, int mid, int high)
{
int LeftSum = INT_MIN;
int Sum = 0;
int MaxLeft = mid;
for (int i = mid; i >= low; i--)
{
Sum = Sum + *(A+i);
if (Sum > LeftSum)
{
LeftSum = Sum;
MaxLeft = i;
}
}
int RightSum = INT_MIN;
int MaxRight = mid+1;
Sum = 0;
for (int j = mid + 1; j <= high; j++)
{
Sum = Sum + *(A+j);
if (Sum > RightSum)
{
RightSum = Sum;
MaxRight = j;
}
}
int *re = (int*)malloc(sizeof(int)*3);
*re = MaxLeft;
*(re + 1) = MaxRight;
*(re + 2) = LeftSum + RightSum;
return re;
}
int * FindMaximunSubarray(int *A, int low, int high)
{
if (low == high)
{
int *re = (int*)malloc(sizeof(int) * 3);
*re = low;
*(re + 1) = high;
*(re + 2) = A[low];
return re;
}
else
{
int mid = (low + high) / 2;
int *Arr1 = FindMaximunSubarray(A, low, mid);
int *Arr2 = FindMaximunSubarray(A,mid+1,high);
int *Arr3 = FindMaxCrossingSubarray(A, low, mid, high);
if (*(Arr1 + 2) > *(Arr2 + 2) && *(Arr1 + 2) > *(Arr3 + 2))
return Arr1;
else if (*(Arr2 + 2) > *(Arr1 + 2) && *(Arr2 + 2) > *(Arr3 + 2))
return Arr2;
else
return Arr3;
}
}
void Print(int * A, int length)
{
int *q = A + length ;
for (int* p = A; p < q; p++)
{
printf("%d ",*p);
}
/*for (int i = 0; i < length; i++)
{
printf("%d ",*(A+i));
}*/
printf("\n");
}
int FindMaxinumSubarray1(int * A, int low, int high)
{
int Sum = 0;
int temp = 0;
for (int i = low; i <= high; i++)
{
temp += A[i];
if (temp > Sum)
Sum = temp;
if (temp < 0)
temp = 0;
}
return Sum;
}