C语言减半递推法求数组最大值,【编程之美】读书笔记:求数组的子数组之和的最大值...

问题:一个有N个整数元素的一维数组(A[0],A[1],A[2],...A[n-1]),这个数组中子数组之和的最大值是多少?

该子数组是连续的。例如 数组:[1,-2,3,5,-3,2]返回8;数组:[0,-2,3,5,-1,2]返回9

解法一:常规解法,时间复杂度为O(N^2)

设Sum[i,...,j]为数组A中第i个元素到第j个元素的和(0<=i<=j利用Sum[i,...,j]=Sum[i,....j-1]+A[j];

int MaxSum(int *A,int n)

{

int maximum=-INF;

int sum;

for(int i=0;imaximum)

maximum=sum;

}

}

return maximum;

}

该算法的时间复杂度为O(N^2)

解法二:采用二分策略(分治法),时间复杂度为(N*logN)。

如果将所给数组(A[0],...,A[n-1])分为长度相等的两段数组(A[0],...,A[n/2-1])和(A[n/2],...,A[n-1]),分别求出这两段数组各自最大子段和,则原数组(A[0],...,A[n-1])的最大子段和分为以下三种情况:

a.(A[0],...,A[n-1])的最大子段和与(A[0],...,A[n/2-1])的最大子段和相同;

b.(A[0],...,A[n-1])的最大子段和与(A[n/2],...,A[n-1])的最大子段和相同;

c.(A[0],...,A[n-1])的最大子段跨过其中间两个元素A[n/2-1]到A[n/2].

对应a和b两个问题是规模减半的两个相同的子问题,可以用递归求得。

对于c,需要找到以A[n/2-1]结尾的和最大的一段数组和S1=(A[i],...,A[n/2-1])和以A[n/2]开始和最大的一段和S2=(A[n/2],...,A[j]),那么第三种情况的最大值为S1+S2。

代码如下:

int MaxSum(const int A[],int Left,int Right)

{

int MaxLeftSum,MaxRightSum; //左、右部分最大连续子序列值。对应情况a、b

int MaxLeftBorderSum,MaxRightBorderSum; //从中间分别到左右两侧的最大连续子序列值,对应c。

int LeftBorderSum,RightBorderSum;

int Center,i;

if(Left == Right) //只有一个元素

if(A[Left]>0)

return A[Left];

else

return 0;

Center=(Left+Right)/2;

MaxLeftSum=MaxSum(A,Left,Center);

MaxRightSum=MaxSum(A,Center+1,Right);

MaxLeftBorderSum=0;

LeftBorderSum=0;

for(i=Center;i>=Left;i--)

{

LeftBorderSum+=A[i];

if(LeftBorderSum>MaxLeftBorderSum)

MaxLeftBorderSum=LeftBorderSum;

}

MaxRightBorderSum=0;

RightBorderSum=0;

for(i=Center+1;i<=Right;i++)

{

RightBorderSum+=A[i];

if(RightBorderSum>MaxRightBorderSum)

MaxRightBorderSum=RightBorderSum;

}

int max1=MaxLeftSum>MaxRightSum?MaxLeftSum:MaxRightSum;

int max2=MaxLeftBorderSum+MaxRightBorderSum;

return max1>max2?max1:max2;

}

解法三:动态规划法(时间复杂度为O(N))

将一个大问题(N个元素数组)转换为一个较小的问题(n-1个元素数组)。假设result[0]为已经找到数组[0,1,...n-1]中子数组和最大的,即保存当前找到的最大子数组。sum[i]为包含第i个元素且和最大的连续子数组。对于数组中的第i+1个元素有两种选择:

a.作为新子数组的第一个元素

b.放入前面已经找到的最大的子数组sum[i-1]中。

int max(int x,int y)

{

return (x>y)?x:y;

}

int MaxSum(int * A,int n)

{

int *sum=(int *)malloc(n*sizeof(int));

int *result=(int *)malloc(n*sizeof(int));

sum[0]=A[0];

result[0]=A[0];

for(int i=1;iA[i]+sum[i-1],则作为新子数组的第一个元素,否则放入前面已经找到的最大的子

数组sum[i-1]中

result[i]=max(sum[i],result[i-1]);

}

return result[n-1];

}

注:前面的算法额外申请了sum和result数组,其实在递推式

sum[i]=max(A[i],A[i]+sum[i-1]);

result[i]=max(sum[i],result[i-1]);中只需要两个变量就可以了。所以只需O(1)的空间。

[

掌握下面这个写法]

int MaxSum(int * A,int n)

{

int sum=A[0];

int result=A[0];

for(int i=1;i

简洁的写法(

掌握):

int MaxSum(int* a, int n)

{

int sum=a[0];

int b=0;

for(int i=0; i

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值