求子数组和的最大值及其时间复杂度分析

典型的输入能帮助我们测试算法的逻辑。
在写具体算法前列出各种可能输入,可以帮助明确题目的要求。

最直接的方法:
记 Sum[i, …, j] 为 数组A中第i个元素到第j个元素的和(其中 0 ≤ i ≤ j < n 0\leq i\leq j< n 0ij<n),遍历所有可能的Sum[i, …, j] ,那么时间复杂度为 O ( N 3 ) O(N^3) O(N3)

int MaxSum(int* A, int n){
	int maximum=-INF;
	int sum;
	for(int i=0;i<n;i++){
		for(int j=i;j<n;j++){
			for(int k=i;k<=j;k++){
				sum+=A[k];
			}
			if(sum>maximum)
				maximum=sum;
		}
	}
	return maximum;
}

改进的直接法:
注意到 Sum[i, …, j]= Sum[i, …, j-1]+A[j],则可以将算法中的最后一个for循环省略,避免重复计算,从而使得算法得以改进,改进后的算法复杂度为 O ( N 2 ) O(N^2) O(N2)

int MaxSum(int* A, int n){
	int maximum=-INF;
	int sum;
	for(int i=0;i<n;i++){
		sum=0;
		for(int j=i;j<n;j++){
			sum+=A[j];
			if(sum>maximum)
				maximum=sum;
		}
	}
	return maximum;
}

分治算法:
把问题分解为两个规模减半的子问题,再采用一次遍历算法。
总的时间复杂度为 T ( N ) = O ( N ∗ l o g 2 N ) T(N)=O(N*log_2N) T(N)=O(Nlog2N)

问题规模减半的相同子问题,可以通过递归求得。

跨越中间元素的处理方法:
两个子数组分别为 (A[0],…,A[n/2-1]) 和 (A[n/2],…,A[n-1])
跨越中间元素的情况只要找到以A[n/2-1]结尾的最大数组和 s 1 = s u m ( A [ i ] , . . . , A [ n / 2 − 1 ] ) ( 0 ≤ i < n 2 − 1 ) s_1=sum(A[i],...,A[n/2-1]) (0\leq i < \frac{n}{2}-1) s1=sum(A[i],...,A[n/21])(0i<2n1)与以 A[n/2] 开始的最大数组和 s 2 = s u m ( A [ n / 2 ] , . . . , A [ j ] ) ( n 2 ≤ j < n ) s_2=sum(A[n/2],...,A[j]) (\frac{n}{2} \leq j < n) s2=sum(A[n/2],...,A[j])(2nj<n)。那么这种情况下的最大值为 s 1 + s 2 = A [ i ] + . . . + A [ n / 2 − 1 ] + A [ n / 2 ] + . . . + A [ j ] s_1+s_2=A[i]+...+A[n/2-1]+A[n/2]+...+A[j] s1+s2=A[i]+...+A[n/21]+A[n/2]+...+A[j],只需对原数组进行一次遍历即可。

动态规划:
选与不选A[0]
考虑数组的第一个元素A[0],以及和最大的一段数组 (A[i],…,A[j]) 跟 A[0] 之间的关系,有以下几种情况:

  1. 当 0=i=j 时,元素 A[0] 本身构成和最大的一段;
  2. 当 0=i<j时,和最大的一段以 A[0] 开始;
  3. 当 0<i 时,元素A[0] 跟和最大的一段没有关系。

m a x { A [ 0 ] , A [ 0 ] + S t a r t [ 1 ] , A l l [ 1 ] } max\{ A[0], A[0]+Start[1], All[1]\} max{A[0],A[0]+Start[1],All[1]}

int max(int x, int y)  //返回x,y两者中较大的值
{
	return (x>y)?x:y;
}

int MaxSum(int* A,int n)
{
	Start[n-1]=A[n-1];
	All[n-1]=A[n-1];
	for(i=n-2;i>=0;i--)  //从数组末尾往前遍历,直到数组首
	{
		Start[i]=max(A[i], A[i]+Start[i+1]);
		All[i]=max(Start[i],All[i+1]);
	}
	return All[0];   //遍历完数组,All[0]中存放着结果
}

上述方法的时间复杂度已经降到 O ( N ) 了 。 O(N)了。 O(N)

思考:上述代码额外申请了两个数组All[],Start[],能否在空间方面节省一些?

进一步改进只需要 O ( 1 ) O(1) O(1)的空间

int max(int x, int y)  //返回x,y两者中较大的值
{
	return (x>y)?x:y;
}

int MaxSum(int* A,int n)
{
//要做参数检查
	nStart=A[n-1];
	nAll=A[n-1];
	for(i=n-2;i>=0;i--)  //从数组末尾往前遍历,直到数组首
	{
		nStart=max(A[i], A[i]+nStart);
		nAll=max(nStart,nAll);
	}
	return nAll;   
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Gallant Hu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值