最大连续子序列和(Java语言版)

题目描述

给定整数A 1 , A 2 , A 3 , . . . , A n A_1,A_2,A_3,...,A_nA1,A2,A3,...,An(可能是负整数),求∑ k = i j A k \sum_{k=i}^{j}A_kk=ijAk的最大值。如果所有的整数均为负数,那么最大子序列的和为0。

简单O ( N 3 ) O(N^3)O(N3)算法

最简单的算法就是直接穷举搜索,或称蛮力算法

public static int maxSubsequenceSum(int[] a) {
   int maxSum = 0;
   for (int i = 0; i < a.length; i++) {
       for (int j = i; j < a.length; j++) {
           int thisSum = 0;
           for (int k = i; k <= j; k++)
               thisSum += a[k];
           if (thisSum > maxSum)
               maxSum = thisSum;
       }
   }
   return maxSum;
}

改进的O ( N 2 ) O(N^2)O(N2)算法

我们试图从算法中删除嵌套循环,这样会降低运行时间。改进算法使用∑ k = i j A k = A j + ∑ k = i j − 1 A k \sum_{k=i}^j A_k=A_j+\sum_{k=i}^{j-1} A_kk=ijAk=Aj+k=ij1Ak。换句话说就是我们已经计算了子序列i , . . . , j − 1 i,...,j-1i,...,j1的和,那么计算子序列i , . . . , j i,...,ji,...,j应该不会花太多时间,我们只需要多家一次计算即可。改进后的算法如下,只使用了两层嵌套循环。运行时间为O ( N 2 ) O(N^2)O(N2)

public static int maxSubsequenceSum(int[] a) {
    int maxSum = 0;
    for (int i = 0; i < a.length; i++) {
        int thisSum = 0;
        for (int j = i; j < a.length; j++) {
            thisSum += a[j];
            if (thisSum > maxSum)
                maxSum = thisSum;
        }
    }
    return maxSum;
}

线性算法

我们可以从分析中排除大量可能的子序列。很显然,最大子序列不可能从负数开始,因此,如果a [ i ] a[i]a[i]为负,那我们可以跳过内循环,并增加i ii。更普遍的,最大子序列不可能以负子序列开始。

public static int maxSubsequenceSum(int[] a) {
    int maxSum = 0;
    int thisSum = 0;
    for (int i = 0, j = 0; j < a.length; j++) {
        thisSum += a[j];
        if (thisSum > maxSum)
            maxSum = thisSum;
        else if (thisSum < 0) {
            i = j + 1;
            thisSum = 0;
        }
    }
    return maxSum;
}

如果我们检测到和为负数,可以将i ii直接变成j jj


版权声明:本文为yu007527原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。