1. 程式人生 > >求陣列的子陣列之和的最大值

求陣列的子陣列之和的最大值

1.問題描述

一個有N個整數元素的一維陣列( A[0], A[1], … , A[n-2], A[n-1]),子陣列之和的最大值是什麼?(要求子陣列的元素是連續的)
在這裡要注意,是陣列的子陣列而不是集合的子集
例子:有陣列( -2, 5, 3, -6, 4, -8, 6),則其子陣列之和的最大值為8,其對應的陣列為(5,3)

2.分析與解法

解法一:採用直接法,記Sum[i…j],為陣列A中從第i到第j之間所有數之和,算出所有Sum,取其最大,程式碼如下,時間複雜度O(N2):

複製程式碼

int maxSum1(int *A, int n)
{
    int max = -1
; int i, j, sum; for(i = 0; i < n; i++) { sum = 0; for(j = i; j < n; j++) { sum += A[j]; if(sum > max ) max = sum; } } return max; }

複製程式碼

解法二:使用分治法,陣列(A[0],A[1],…A(n-1)分為長度相等的兩段陣列(A[0],…,A[n/2-1])以及(A[n/2],…,A[n-1]),分別求出這兩段陣列各自的最大子段和,則原陣列(A[0],A[1],…A(n-1)的最大子段和分為3種情況

1).(A[0],A[1],…A(n-1)的最大子段和與(A[0],…,A[n/2-1])的相同

2).(A[0],A[1],…A(n-1)的最大子段和與(A[n/2],…,A[n-1])的相同

3).(A[0],A[1],…A(n-1)的最大子段和跨過(A[0],…,A[n/2-1])與(A[n/2],…,A[n-1])-

1)和2)可以根據遞迴可得,3)只要計算出以A[n/2-1]為結尾的一段陣列最大和s1=Sum1[i…n/2-1]和A[n/2]為開頭一段陣列最大和s2=Sum2[n/2…j],最後s=s1+s2.

這個演算法滿足分值演算法遞迴,總的時間複雜度O(N*log2N)

解法三:假設我們已經知道(A[k]…..A[n-1])最大的一段陣列和為All[k],並且已經計算出在(A[k]…..A[n-1])中包含A[k]的最大的一段陣列和為Start[k],那麼可以推斷出All[k-1]=max{A[k-1],A[k-1]+Start[k],All[k]},利用動態規劃思想以及這樣的遞推公式,從後往前計算,程式碼如下,時間複雜度O(N):

int max(int x, int y)
{
    return (x > y) ? x : y;
}

int maxSum2(int *A, int n)
{
    int i;
    int All[n], Start[n];

    All[n-1] = A[n-1];
    Start[n-1] = A[n-1];

    for(i = n-2; i >= 0; i--)
    {
          Start[i] = max(A[i], A[i]+Start[i+1]);
          All[i] = max(All[i+1], Start[i]);
    }

    return All[0];    
}

對以上程式碼進行簡化,因為最後所求到的變數只有Start[0]和All[0],這樣可以反覆用nStart和nAll,省略掉其他的變數,程式碼如下:

int max(int x, int y)
{
    return (x > y) ? x : y;
}

int maxSum2_v(int *A, int n)
{
    int i;
    int nAll, nStart;

    nAll = A[n-1];
    nStart = A[n-1];

    for(i = n-2; i >= 0; i--)
    {
          nStart = max(A[i], A[i]+nStart);
          nAll = max(nAll, nStart);
    }

    return nAll;    
}

注:以上的計算順序也可以從前往後,即:All[k+1]=max{A[k+1],A[k+1]+Start[k],All[k]}.