最大子数组问题–分治策略
参考资料:算法导论 P38.
所谓分治,一共分为三步:
分解:分解步骤将问题划分为一系列子问题,子问题和原问 题形式一样,只是规模更小
解决:递归地求解出子问题,如果子问题足够小 ,就停止递归,直接求解
合并:将子问题的解组合城原问题的解
现在给一个整数型数组。求出一一个它的子数组世得子数组里的元素和最大。
先分解这个问题。
假定我们要寻找子数组A[low->high]的最大子数组
我们可以将这个数组从中间分开。得到两个数组。所求的最大子串的纯在形式有三种:
1.完全位于A[low->mid].
2.完全位于A[mid+1->high]
3.被mid所截。
我们可以分别求出以上三种情况的最大值left_sum,right_sum和cross_sum,然后三者中的最大值就是所求答案。
对于1和2情况,很明显为问题的子问题。对于第三中情况,我们以mid为分界点,分别向左和向右进行递加,求的每一边的最大值,加起来即为第三种情况的最大值 。
c代码如下:
#include<stdio.h>
#include<stdlib.h>
//子数组中有mid
int find_max_crossing_subarray(int *A, int low, int high, int mid)
{
int left_sum = -0xffff; //保存到目前为止左边最大和
int sum = 0; //保存A[i->mdi]中所有值的和
for (int i = mid; i >= low; i--)
{
sum += A[i];
if (sum > left_sum)
left_sum = sum;
}
int right_sum = -0xffff; //保存到目前为止右边最大和
sum = 0;
for (int i = mid + 1; i <= high; i++)
{
sum += A[i];
if (sum > right_sum)
right_sum = sum;
}
return left_sum + right_sum;
}
int find_maximum_subarray(int *A, int low, int high)
{
if (high == low) //数组里只有一个元素
return A[low];
int mid = (high + low) / 2; //得到中间元素的索引
int left_sum = find_maximum_subarray(A, low, mid);
int right_sum = find_maximum_subarray(A, mid + 1, high);
int cross_sum = find_max_crossing_subarray(A, low, high, mid);
//取三个值中最大值
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,-4,7 };
int sum = find_maximum_subarray(A, 0, 15);
printf("%d\n", sum);
}