此处介绍四种方法,复杂度in descending order
一、O(n^3)
普通做法;
即在数组中,遍历起始和终止位置,然后从起始到终止位置累加求和,求得的sum与最大值比较,如果比最大值大,就赋值;
int best = 0;//ans
for(int i = 1; i <= n; i++)
for(int j = i; j <= n; j++)
{
int sum = 0;
for(int k = i; k <= j; k++) sum += A[k];
if(sum > best) best = sum;
}
二、O(n^2)
用前缀和,从起始到终止的求和过程所求得的sum,即为终止的前缀和减去(起始位置-1)的前缀和所得的值;
那么在开始遍历前,我们只要先求出前缀和,然后再遍历,用前缀和来减就可以;
s[0] = 0;
for(int i = 1; i <= n; i++)
s[i] = s[i-1] + A[i];
for(int i = 1; i <= n; i++)
for(int j = i; j <= n; j++)
maxs = max(maxs, s[j] - s[i-1]);
三、O(nlogn)
分治的思想,找到左一半的最大连续和和右一半的最大连续和,然后归并;
int maxm(int *A, int x, int y)
{
if(y - x == 1) return A[x];
int m = x + (y - x)/2;//划分区间,[x,m),[m,y);
int maxs = max(maxm(A,x,m), maxm(A,m,y));//继续划分区间;
int v, L, R;
v = 0;
L = A[m-1];
for(int i = m-1; i >= x; i--) L = max(L, v+=A[i]);//左一半的最大连续和;
v = 0;
R = A[m];
for(int i = m; i <= y; i++) R = max(R, v+=A[i]);//右一半的最大连续子段和;
return maxs = max(maxs,L+R);
}
对于分治时间复杂度的计算
T(n) = 2T(n/2) + n;
…… = 2(2T(n/4) + n/2) +n;
…… = 4T(n/4) + 2n;
…… = 4(2*(T(n/8) + n/4) + 2n;
…… = 8T(n/8) + 3n;
…… = 2^kT(n/2的k次方) + kn;//直到2^k== n;此时k = log(2,n);
…… = nT(1) + nlog(2,n);
…… = nlogn + n;
四、O(n)
第四种方法是在第二种方法的基础上加以改进,实际上我们求最大连续子段和,就是求最大的s[j] - s[i-1],那么当j确定时,要使s[j] - s[i - 1]最大,那么s[i-1]应该最小,只要遍历前缀和数组维护最小的s就可以;
s[0] = 0;
for(int i = 1; i <= n; i++)
s[i] = s[i-1] + A[i];
int mins = 0;
for(int i = 1; i <= n; i++)
{
mins = min(mins,s[i-1])
best = max(best,s[i] - mins);
}