最大连续子数组和

题目描述

给出一个长度为n的序列A1, A2,···, An, 求最大连续和。换句话说,要求找到1<=i<=j<=n, 使得Ai + Ai+1 + ···+ Aj 尽量大。


蛮力枚举

int MaxSubArray(int* A, int n)
{
    int maxSum = a[0];
    int currSum = 0;
    for(int I = 0; I < n; I++)
    {
        for(int j = I; j < n; j++)
        {
            for(int k = I; k <= j; k++)
            {
                currSum += A[k];
            }
            if(currSum > maxSum)
                maxSum = currSum;
            //要记得清零,否则sum最终存放的是所有子数组的和
            currSum = 0;
        }
    }   
    return maxSum;  
}

此方法的时间复杂度为O(n^3)。


递推法

试着优化下这个算法。设 Si = A1 + A2 + ··· + Ai, 则 Ai + Ai+1 + ··· + Aj = Sj - Si-1。这个式子的用途相当广泛,其直观含义是“连续子序列之和等于两个前缀和之差”。有了这个结论,最内层的循环就可以忽略了。

S[0] = 0;
for(I = 1; I <= n; I++) 
    S[i] = S[i-1] + A[i]; //递推前缀和 S
for(I = 1; I <= n; I++)
    for(j = I; j <= n; j++)
        best >?= S[j] - S[I-1];  //更新最大值

“计算S”时间复杂度为O(n), 二重循环的时间复杂度O(n^2)。


分治法

  • 划分问题: 把问题的实例划分成子问题。
  • 递归求解: 递归解决子问题。
  • 合并问题: 合并子问题的解得到原问题的解。
int maxsum(int* A, int x, int y) //返回数组在左闭右开区间[x,y)中的最大连续和
{
    int i, m, v, L, R, max;
    if(y - x == 1)
        return A[x]; //只有一个元素,直接返回

    m = x + (y-x)/2; //分治第一步:划分成[x,m) 和[m,y)    

    max = maxsum(A, x, m) >? maxsum(A, m ,y); //分治第二步:递归求解
    v = 0;
    L = A[m-1]; //分治第三步:合并(1)——从分界点开始往左的最大连续和L

    for(I = m -1; I >= x; I--)
        L >? v += A[I];
    v = 0;
    R = A[m]; //分治第三步:合并(2)——从分界点开始往右的最大连续和R

    for(I = m; I < y; I ++)
        R >? v += A[I];

    return max >? (L+R); //把子问题的解与L和R比较

}

时间复杂度O(NlogN)


动态规划(beautiful)

令currSum是以当前元素结尾的最大连续子数组的和, maxSum是全局的最大子数组的和, 当往后扫描时, 对第 j 个元素有两种选择,要么放入前面找到的子数组, 要么作为新子数组的第一个元素:

  • 如果 currSum > 0, 则令currSum + a[j];
  • 如果currSum < 0, 则currSum 被置为当前元素,即currSum = a[j]

举例说明,a = {1, -2, 3, 10, -4, 7, 2, -5}.
currSum: 0 → 1 → -1 → 3 → 13 → 9 → 16 → 18 → 13
maxSum: 0 → 1 → 1 → 3 → 13 → 13 →16 → 18 → 18

int MaxSubArray(int* a, int n)
{
    int currSum = 0;
    int maxSum = a[0];

    for(int j = 0; j< n; j++)
    {
        if(currSum > 0)
            currSum += a[j];
        else
            currSum = a[j];

        if(currSum > maxSum)
            maxSum = currSum;
    }
    return maxSum;
}

从前往后扫描一遍数组,即完成求最大连续子数组和的需求,所以时间复杂度为 O(n)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值