最大子数组问题
假设数组a[low..high],求存在于数组a中的连续子数组之和(或数组中连续元素之和)最大的子数组,例如a[4]={3,-2,1,-2}的最大子数组为a[0->2]和最大为3+(-2)+1=2。
如果采用暴力求解的算法则问题也可以很简单的求解出来,不过这种方法的时间复杂度为Ω(n²),所以写一下更好的方法。
使用分治策略的求解方法
直接引用书上的语言(我认为书上总结的比我好),假定我们要寻找子数组A[low..high]的最大子数组。使用分治技术意味着我们要将子数组划分为两个规模尽量相等的组数组,也就是说,找到子数组的中央位置,比如mid,然后考虑求解两个子数组A[low..mid]和A[mid+1..high]。A[low..high]的任何连续子数组A[i..j]所处的位置必然是一下三种情况。
完全位于数组A[low..mid]中,因此low<=i<=j<=mid。
完全位于数组A[mid+1..high]中,因此mid<i<=high。
跨越了中点,因此 low<=i<=mid<=j<=high。
因此A[low..high]的一个最大子数组所处的位置必然是这三种情况之一。
自己写的代码如下欢迎指正
/*
程序中low为数组下界,high为上界,mid为中间,sum为和
*/
#include <stdio.h>
//定义结构体包含子数组的上界,下界,和
//为了接收返回子数组的上界,下界与数组和这三个参数,我采用了结构体的方法解决了c中return只能返回一个参数的问题。
typedef struct{
int low;
int high;
int sum;
}Subarry;
Subarry Find_Max_Crossing_Subarry(int*a,int low,int mid,int high);//求解跨越了中点的
Subarry Find_Maximum_Subarry(int*a,int low,int high);//完全位左右子数组中
int main ()
{
int a[7]={-2,-1,-3,-4,-2,1,3};//测试数据
Subarry result;
int length=sizeof(a)/sizeof(int);
result=Find_Maximum_Subarry(a,0,length-1);
//打印原数组
printf("原数组为:");
for(int i=0;i<length;i++)
{
printf("%d ",a[i]);
}
//打印最大子数组
printf("\n最大子数组为:");
for(i=result.low;i<=result.high;i++)
{
printf("%d ",a[i]);
}
printf("\n最大子数组的和为:%d \n最大子数组的下标为:%d->%d\n",result.sum,result.low,result.high);
return 0;
}
//完全位左右子数组中
Subarry Find_Maximum_Subarry(int*a,int low,int high)
{
Subarry subarryone,subarryleft,subarryright,subarrycross;
int mid;
if(low == high)//只有一个元素的情况
{
subarryone.low=low;
subarryone.high=high;
subarryone.sum=a[high];
return subarryone;
}
else
{
mid=(low+high)/2;
subarryleft = Find_Maximum_Subarry(a,low,mid);
subarryright = Find_Maximum_Subarry(a,mid+1,high);
subarrycross = Find_Max_Crossing_Subarry(a,low,mid,high);
if(subarryleft.sum > subarryright.sum && subarryleft.sum > subarrycross.sum)
{
return subarryleft;
}
else if(subarryright.sum > subarryleft.sum && subarryright.sum > subarrycross.sum)
{
return subarryright;
}
else
{
return subarrycross;
}
}
}
//求解跨越了中点的
Subarry Find_Max_Crossing_Subarry(int*a,int low,int mid,int high)
{
Subarry cross;
int left_sum = -1000;
int sum = 0;
int max_left,max_right;
int i;
max_left=mid;
for(i=mid;i >= 0;i--)
{
sum=sum+a[i];
if(sum > left_sum)
{
left_sum = sum;
max_left=i;//记录下界
}
}
int right_sum = -1000;
sum = 0;
max_right=mid+1;
for(i=mid+1;i<=high;i++)
{
sum=sum+a[i];
if(sum > right_sum)
{
right_sum = sum;
max_right=i;//记录上界
}
}
//返回值
cross.low=max_left;
cross.high=max_right;
cross.sum=right_sum+left_sum;
return cross;
}