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


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




  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值