Chapter 2-最大子序列和问题的解

题目:给定整数(可能有负数),求的最大值(为方便起见,如果所有整数均为负数,则最大子序列和为0)。

例:输入-2,11,-4,13,-5,-2时,答案为20。

分析:这个问题之所以有吸引力,主要是因为存在求解它的很多算法,而这些算法的性能又差异很大。该问题源于《数据结构与算法分析--C语言描述》第二章“算法分析”,该问题是算法分析入门的经典例子。有4种典型的算法,它们的时间复杂度分别是


1、

int MaxSubsequenceSum (const int A[], int N)
{
    int ThisSum, MaxSum, i, j, k;
    
    MaxSum = 0;/*1*/
    for (i = 0; i < N; i++)/*2*/
        for (j = i; j < N; j++)/*3*/
        {
            ThisSum = 0;/*4*/
            for (k = i; k <= j; k++)/*5*/
                ThisSum += A[k];/*6*/
            if (ThisSum > MaxSum)/*7*/
                MaxSum = ThisSum;/*8*/
        }
    return MaxSum;/*9*/
}
运行时间为 ,这完全取决于第5行和第6行,第6行由一个含于三重嵌套for循环中的 语句组成。第1个循环大小为 。第2个循环大小为 ,它可能要小,但也可能是 。我们必须假设最坏的情况,而这可能会使最终的界有些大。第3个循环的大小为 ,我们也要假设它的大小为 。因此总数为 。语句1总共的开销只是 ,而语句7和8总共开销也只不过 ,因为它们只是两层循环内部的简单表达式。


2、

我们可以通过撤出一个for循环来避免立方运行时间。算法1中第5行和第6行上的计算过分地耗时了,改进后的算法时间复杂度为

int MaxSubsequenceSum (const int A[], int N)
{
    int ThisSum, MaxSum, i, j;
    
    MaxSum = 0;/*1*/
    for (i = 0; i < N; i++)/*2*/
    {
        ThisSum = 0;/*3*/
        for (j = i;j < N; j++)/*4*/
        {
            ThisSum += A[j];/*5*/
            
            if (ThisSum > MaxSum)/*6*/
                MaxSum = ThisSum;/*7*/
        }
    }
    return MaxSum;/*8*/
}

3、

分治策略:最大子序列和可能在三处出现。或者整个出现在输入数据的左半部,或者整个出现在右半部,或者跨越输入数据的中部从而占据左右两个部分。前两种情况可以递归求解。第三种情况的最大和可以通过求出前半部分的最大和(包含前半部分的最后一个元素)以及后半部分的最大和(包含后半部分的第一个元素)而得到。然后将这两个和加在一起。算法如下:

static int MaxSubSum (const int A[], int Left, int Right)
{
    int MaxLeftSum, MaxRightSum;
    int MaxLeftBorderSum, MaxRightBorderSum;
    int LeftBorderSum, RightBorderSum;
    int Center, i;
    
    if (Left == Right)/*1*/
        if (A[Left] > 0)/*2*/
            return A[Left];/*3*/
        else
            return 0;/*4*/
    
    Center = (Left + Right) / 2;/*5*/
    MaxLeftSum = MaxSubSum(A, Left, Center);/*6*/
    MaxRightSum = MaxSubSum(A, Center + 1, Right);/*7*/
    
    MaxLeftBorderSum = 0; LeftBorderSum = 0;/*8*/
    for (i = Center; i >= Left; i--)/*9*/
    {
        LeftBorderSum += A[i];/*10*/
        if (LeftBorderSum > MaxLeftBorderSum)/*11*/
            MaxLeftBorderSum = LeftBorderSum;/*12*/
    }
    
    MaxRightBorderSum = 0; RightBorderSum = 0;/*13*/
    for (i = Center + 1; i <= Right ; i++)/*14*/
    {
        RightBorderSum += A[i];/*15*/
        if (RightBorderSum > MaxRightBorderSum)/*16*/
            MaxRightBorderSum = RightBorderSum;/*17*/
    }
    
    return Max3(MaxLeftSum, MaxRightSum, MaxLeftBorderSum + MaxRightBorderSum);/*18*/
}

int MaxSubsequenceSum(const int A[], int N)
{
    return MaxSubSum(A, 0, N - 1);
}

是求解大小为 的最大子序列和问题所花费的时间。如果 ,则算法3执行程序第1行到第4行花费某个时间常量,我们称之为一个时间单元。于是, 。否则,程序必须运行两次递归调用,即在第9行和第17行之间的两个for循环,还需要某个小的簿记量,如在第5行和第18行。这两个for循环总共接触到从 的每一个元素,而在循环内部的工作量是常量,因此,在第9到第17行花费的时间为 。第1行到第5行,第8、13和18行上的程序的工作量都是常量,从而与 相比可以忽略。其余就是第6、7行上运行的工作。这两行求解大小为 的子序列问题(假设 是偶数)。因此,这两行每行花费 个时间单位,共花费 个时间单位。算法3花费的总的时间为 。为了简化计算,我们可以用 代替上面方程中的 ;由于最终还是要用大 来表示的,因此这么做并不影响答案。已知 ,那么 ,以及 。其形式是显然的并且可以得到,即若 ,则


4、

int MaxSubsequenceSum(const int A[], int N)
{
    int ThisSum, MaxSum, j;
    
    ThisSum = MaxSum = 0;/*1*/
    for (j = 0; j < N; j++)/*2*/
    {
        ThisSum += A[j];/*3*/
        
        if (ThisSum > MaxSum)/*4*/
            MaxSum = ThisSum;/*5*/
        else if (ThisSum < 0)/*6*/
            ThisSum = 0;/*7*/
    }
    return MaxSum;/*8*/
}

该算法叫做联机算法(online algorithm),其特点是, 在任意时刻,算法都能对它已经读入的数据给出子序列问题的答案。仅需要常量空间并以线性时间运行的联机算法几乎是完美的算法!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值