求数组的子数组之和的最大值
2015-09-04 09:24
197 查看
转载自:http://blog.csdn.net/xiaoding133/article/details/8038208
问题:一个有N个整数元素的一维数组(A[0],A[1],A[2],...A[n-1]),这个数组中子数组之和的最大值是多少?
该子数组是连续的。例如 数组:[1,-2,3,5,-3,2]返回8; 数组:[0,-2,3,5,-1,2]返回9
[cpp]
view plaincopyprint?
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;
}
该算法的时间复杂度为O(N^2)
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。
代码如下:
[cpp]
view plaincopyprint?
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;
}
a.作为新子数组的第一个元素
b.放入前面已经找到的最大的子数组sum[i-1]中。
[cpp]
view plaincopyprint?
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;i<n;i++)
{
sum[i]=max(A[i],A[i]+sum[i-1]);//若A[i]>A[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)的空间。
[掌握下面这个写法]
[cpp]
view plaincopyprint?
int MaxSum(int * A,int n)
{
int sum=A[0];
int result=A[0];
for(int i=1;i<n;i++)
{
sum=max(A[i],A[i]+sum);
result=max(sum,result);
}
return result
}
简洁的写法(掌握):
[cpp]
view plaincopyprint?
int MaxSum(int* a, int n)
{
int sum=a[0];
int b=0;
for(int i=0; i<n; i++)
{
if(b<0) ////
b=a[i]; //小于0,重新计算起点
else
b+=a[i]; //大于0,就想加
if(sum<b) //保存最大的和
sum=b;
}
return sum;
}
问题:一个有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<n),遍历所有的Sum[i,...,j],并且利用Sum[i,...,j]=Sum[i,....j-1]+A[j];[cpp]
view plaincopyprint?
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;
}
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; }
该算法的时间复杂度为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。
代码如下:
[cpp]
view plaincopyprint?
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;
}
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]中。
[cpp]
view plaincopyprint?
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;i<n;i++)
{
sum[i]=max(A[i],A[i]+sum[i-1]);//若A[i]>A[i]+sum[i-1],则作为新子数组的第一个元素,否则放入前面已经找到的最大的子
数组sum[i-1]中
result[i]=max(sum[i],result[i-1]);
}
return result[n-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;i<n;i++) { sum[i]=max(A[i],A[i]+sum[i-1]);//若A[i]>A[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)的空间。
[掌握下面这个写法]
[cpp]
view plaincopyprint?
int MaxSum(int * A,int n)
{
int sum=A[0];
int result=A[0];
for(int i=1;i<n;i++)
{
sum=max(A[i],A[i]+sum);
result=max(sum,result);
}
return result
}
int MaxSum(int * A,int n) { int sum=A[0]; int result=A[0]; for(int i=1;i<n;i++) { sum=max(A[i],A[i]+sum); result=max(sum,result); } return result }
简洁的写法(掌握):
[cpp]
view plaincopyprint?
int MaxSum(int* a, int n)
{
int sum=a[0];
int b=0;
for(int i=0; i<n; i++)
{
if(b<0) ////
b=a[i]; //小于0,重新计算起点
else
b+=a[i]; //大于0,就想加
if(sum<b) //保存最大的和
sum=b;
}
return sum;
}
相关文章推荐
- 20150904看电影学英语
- 测试代码片看看
- 苹果和虫子
- 跟Android自带模拟器说拜拜,Mac Genymotion 使用心得
- HDU1241 Oil Deposits
- hdu5179 数位DP
- Cocos2d-x Expression: child != nullptr
- html中有关如何固定div大小的问题
- Android 使用Component跨应用程序访问
- Python isinstance用法
- Expression Tree Introduction - 02
- bash下. : () {} [] [[]] (())的解释
- 剑指offer之树的子结构
- C++中直接管理内存的方式
- 两只完全相同的宠物
- 在rust里使用unstable feature
- 【HDU1151】【二分匹配】【最小路径覆盖】【建模】
- linux下mysql的root密码忘记解决方法
- Python_基础01
- Objective-C中ORM的运用:实体对象和字典的相互自动转换