题目:输入一个整形数组,数组里有正数也有负数。
数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。
求所有子数组的和的最大值。要求时间复杂度为O(n)。
1.最直接的方法,时间复杂度为O(N^2)
2.时间复杂度为O(N)。不断的累加每个数组元素,并用一个变量保存当前的最大值,累加的过程一直和该变量进行比对,如果大于最大值,就把当前的最大值保存下来,反复如此就可以求出最大值.
#include<iostream>
using namespace std;
int MaxSum1(int *A,int n)
{
int maxSum=0;
int sum;
for (int i=0;i<n;i++)
{
sum=0;
for (int j=i;j<n;j++)
{
sum +=A[j];
if (sum>maxSum)
maxSum=sum;
}
}
return maxSum;
}
int MaxSum2(int* a, int n)
{
int nSum = 0;
int nValue=0;
for(int i = 0; i < n; i++)
{
if (nValue <= 0)
nValue = a[i];
else
nValue += a[i];
if(nSum < nValue)
nSum = nValue;
}
return nSum;
}
int main(int argc,char* argv[])
{
int anAry[] = {1, -2, 3, 10, -4, 7, 2, -5};
int nMaxValue = MaxSum1(anAry, sizeof(anAry)/sizeof(int));
cout << "最大的子数组和为:" << nMaxValue << endl;
int nMaxValue2 = MaxSum2(anAry, sizeof(anAry)/sizeof(int));
cout << "最大的子数组和为:" << nMaxValue2 << endl;
return 0;
}
扩展问题
#include <iostream>
using namespace std;
int max(int x,int y)
{
return (x>y)?x:y;
}
int min(int x,int y)
{
return (x<y)?x:y;
}
//*********************************动态规划**************************************
//假设A[0],A[1],...A(n-1)的最大子段为A[i],...,A[j],则有以下3种情况,
//1)当0=i=j的时候,元素A[0]本身构成和最大的一段
//2)当0=i<j的时候,和最大的一段以A[0]开头
//3)当0<i时候,元素A[0]跟和最大的一段没有关系
//则原始问题A[0],A[1],...A(n-1)的解All[0]=max{A[0],A[0]+Start[1],ALL[1]}
//求得A[0],A[1],...A[n-1](首尾不连接)的情况后再考虑整体思路中的第二种情况
//*********************************************************************************/
//从尾到首动态规划
int MaxSum(int *A,int length)
{
//先求出A[0],A[1],...A[n-1](首尾不连接)的情况下子数组和最大值nAll
int nStart=0;
int nAll=0;
for(int i=length-1;i>=0;i--)
{
nStart=max(0,A[i]+nStart);
nAll=max(nStart,nAll);
}
//下面处理整体思路的第二种情况,即跨过A[n-1],A[0]
//先求A[n-1]结尾的和最大的一段(A[i],...,A[n-1])(0<=i<n)
int sum=0;
int ltempmax=-10000000;
int lpos=length;
for(int i=length-1;i>=0;i--){
sum+=A[i];
if(sum>ltempmax){
ltempmax=sum;
lpos=i;
}
}
//求A[0]开始和最大的一段(A[0],...,A[j])(0<=j<n)
sum=0;
int rtempmax=-10000000;
int rpos=-1;
for(int i=0;i<length;i++)
{
sum+=A[i];
if(sum>rtempmax)
{
rtempmax=sum;
rpos=i;
}
}
//如果lpos<=rpos,则循环数组中可能出现的子数组最大值要么是A[0]...A[n-1]子数组和的最大值nAll
//要么是整个数组A[0]...A[n-1]的和再减去A[0]...A[n-1]中子数组和为负数的最小值
if(lpos<=rpos)
{
//求数组中和为负数且的最小值
int minStart=0;
int minAll=0;
for(int i=0;i<length;i++)
{
minStart=min(0,A[i]+minStart);
minAll=min(minStart,minAll);
}
int tempmax=ltempmax+rtempmax;
for(int i=lpos;i<=rpos;i++)
{
tempmax-=A[i];
}
//比较A[0]...A[n-1]子数组和的最大值nAll跟A[0]...A[n-1]的和再减去A[0]...A[n-1]中子数组和为负数的最小值
return max(nAll,tempmax-minAll);
}else{
//比较A[0]+...+A[j]+A[i]+...+A[n-1]即ltempmax+rtempmax的值跟A[0]...A[n-1]子数组和的最大值nAll
return max(nAll,ltempmax+rtempmax);
}
}
int main(int argc,char* argv[])
{
int anAry[] = {-1, 2, 3, 10, 4, -7, 2, 5};
int nMaxValue = MaxSum(anAry, sizeof(anAry)/sizeof(int));
cout << "最大的子数组和为:" << nMaxValue << endl;
return 0;
}
2.如果题目要求同时返回最大字数组的位置,算法应如何改变,还能保持O(N)的时间复杂度吗?
#include<iostream>
using namespace std;
int MaxSum(int* a, int len, int &m, int &n)
{
int nSum = 0;
int nValue=0;
int k;
for(int i = 0; i < len; i++)
{
if (nValue <= 0)
{
nValue = a[i];
k=i;
}
else
nValue += a[i];
if(nSum < nValue)
{
nSum = nValue;
m=k;n=i;
}
}
return nSum;
}
int main(int argc,char* argv[])
{
int anAry[] = {1, -2, 3, 10, -4, 7, 2, -5};
int m,n;
int nMaxValue = MaxSum(anAry, sizeof(anAry)/sizeof(int),m,n);
for (int i=m;i<=n;i++)
cout<<anAry[i]<<", ";
cout<<endl;
cout << "最大的子数组和为:" << nMaxValue << endl;
return 0;
}