最大子数组问题

小渣今天再来更新一波,欢迎看众 老爷们尽情喷(批评指导)

何为最大子数组:简言之,就是一数组A[n]中从i到j的连续子序列(i,j均在(0,n)区间内),且不存在另一对(a,b)对使得A[a]+A[a+1]+.....A[b]>A[i]+A[i+1]+...+A[j]

可见:①只有在数组含有负数元素时这个问题才有探讨的价值,否则整个数组本身即是最大子数组了;

②并且,i<=j(最大子数组可能只含有一个元素)

③最大子数组可能不唯一

解法一:

思路:按照二分法来做,最大子数组两端无非有三种情况:

在左半部分,在右半部分,包含中点位置元素

找出三种情形中子数组和最大的那个作为结果,只有前两种情况是可以递归的哦

复杂度分析:二分法

T(n)=Θ(1)(n=1)

      =2*T(n/2)+Θ(n):二分做了左右两半,加上包含中间位置元素的subarray查找,该查找为线性时间

T(n)=Θnlgn

# include <iostream.h>
# include "..\Sort\IO_tools.cpp"
/*
subarray封装一次查找中得到的最大子数组的两端和该子数组的元素之和
*/
struct subarray{
  int l,r,sum;
};
subarray Find_Max_Cross_SubArray(int A[],int l,int r);//寻找数组A中从l到r索引区间内包含该子数组中点位置的最大子数组
subarray Find_Max_SubArray(int A[],int l,int r);//递归查找数组A中从l到r索引区间内的最大子数组(取三种情况中子数组元素之和最大的作为返回值)
void main(){
    /*
	初值化测试对象
	*/
  int A[6];
  inputA(A,6);
  outputA(A,6);
  /*
  得到最大子数组
  */
  subarray Sub=Find_Max_SubArray(A,0,5);
  /*
  输出
  */
  cout<<"Result:"<<Sub.l<<"  "<<Sub.r<<"  "<<Sub.sum<<endl;
}
subarray Find_Max_SubArray(int A[],int l,int r){
	subarray s1,s2,s3,s4;
	/*
	对于包含两个以上元素的子数组,都要拆半计算
	*/
	if(l<r){
		int mid=(l+r)/2;
		s1=Find_Max_SubArray(A,l,mid);//情况一
		s2=Find_Max_SubArray(A,mid+1,r);//情况二
		s3=Find_Max_Cross_SubArray(A,l,r);//情况三
		/*
		得到l,r索引区间内A的最大子数组
		*/
		s4=s1.sum>=s2.sum?s1:s2;
		s4=s4.sum>=s3.sum?s4:s3;
		/*
		对于含有两个(或以上)元素的区间输出一条调试信息,可以核查自己的思路和代码之间有没有出入,并且加深领会算法思路
		*/
		cout<<"analyse-:"<<s3.l<<"  "<<s3.r<<"  "<<s3.sum<<endl;
	}
	/*
	只包含一个元素:拆分到底了,即到达了递归的边界
	*/
	else{
		s4.l=s4.r=l;
		s4.sum=A[l];
	}
	/*
	输出本次查找的结果
	*/
    cout<<"analyse:"<<s4.l<<"  "<<s4.r<<"  "<<s4.sum<<endl;
	return s4;
}
/*
因为横跨两部分的子数组显然不是递归解决的,自然是要拉出来单独做:从中点向左最大子数组并上从中点向右子数组即是横跨两半的最大子数组
*/
subarray Find_Max_Cross_SubArray(int A[],int l,int r){
	int left_sum,right_sum,max_left,max_right,sum,mid;
	subarray s;
	left_sum=right_sum=-999;//当做-负无穷看好了,再小也不会比中点元素小,所以这个“负无穷”不会传递到结果中去
	sum=0;
	mid=(r+l)/2;
	for(int i=mid;i>=l;i--){//从中点开始遍历左半部分
	    sum=sum+A[i];
		if(sum>left_sum){
		   left_sum=sum;//只在和增大的情况下更新记录
		   max_left=i;
		}
	}
	sum=0;
	/*分析方法同上*/
	for(int j=mid+1;j<=r;j++){
	    sum+=A[j];
		if(sum>right_sum){
		    right_sum=sum;
			max_right=j;
		}
	}
	/*合并结果*/
	s.l=max_left;
	s.r=max_right;
	s.sum=left_sum+right_sum;
	return s;
}

 解法二:

思路:对A(i,j+1),其最大子数组为Max{A(i,j)的最大子数组,包含j+1节点向左的最大子数组}

复杂度:1+2+3+....+n=Θ(n²)

# include <iostream.h>
# include "..\Sort\IO_tools.cpp"
struct subinfo{
  int l,r,sum;
};
subinfo Find_Max_SubArray(int A[],int l,int r);
subinfo Find_IncludeThis_MaxSubArray(int A[],int r);
void main(){
  int A[6];
  while(true){
     inputA(A,6);
     outputA(A,6);
     subinfo s=Find_Max_SubArray(A,0,5);
     cout<<"Result:"<<s.l<<" "<<s.r<<" "<<s.sum<<endl;
  }
}
subinfo Find_Max_SubArray(int A[],int l,int r){
	subinfo s1,s2;
	if(l<r){
	   s1=Find_Max_SubArray(A,l,r-1);
	   s2=Find_IncludeThis_MaxSubArray(A,r);
	   cout<<"Analyse1:"<<s1.l<<" "<<s1.r<<" "<<s1.sum<<endl;
	   cout<<"Analyse2:"<<s2.l<<" "<<s2.r<<" "<<s2.sum<<endl;
	   s1=s1.sum>s2.sum?s1:s2;
	   cout<<"Analyse3:"<<s1.l<<" "<<s1.r<<" "<<s1.sum<<endl;
	}
	else{
	   s1.l=s1.r=l;
	   s1.sum=A[l];
	}
	return s1;
}
subinfo Find_IncludeThis_MaxSubArray(int A[],int r){
   subinfo s;
   int maxsum=-9999,sum=0,maxleft=r;
   for(int i=r;i>=0;i--){
	   sum+=A[i];
		   if(sum>maxsum){
		        maxsum=sum;
				maxleft=i;
		   }
   }
   s.l=maxleft;
   s.r=r;
   s.sum=maxsum;
   return s;
}

  

该问题的实际应用:

算法导论分治策略里的一个例子,大家自己看就好

 

转载于:https://www.cnblogs.com/zpfly2008/p/5571372.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值