您的位置:首页 > 编程语言

[算法导论-分治策略]求最大子数组之各种解法及源代码实现

2017-07-12 13:46 519 查看
/*

问题: 已知数组 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);



Find_Maximun_subarray(A, low, high)

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息