1. 程式人生 > >分而治之的思想--最大子列和問題

分而治之的思想--最大子列和問題

問題描述:給定N個整數序列,{A1,A2,…,An},求該序列中存在的最大的連續n個整數和

要解決這個問題,我們很容易想到,可以把所有的連續子列和全部算出來,然後從中找出最大的一個即為所求答案,演算法程式碼如下:

原始程式碼

int maxSubseqSum1(int A[],int N)
{
  int i,j,k;
  int ThisSum,MaxSum = 0;
  for( i = 0; i<N; i++)
  {
    /*i是子列左端位置*/
    for( j = i; j<N; j++ )
    {
       /*j是子列右端的位置*/
       ThisSum = 0
;/*ThisSum是從A[i]到A[j]的子列和*/ for( k = i; k <=j; k++ ) ThisSum += A[k]; if(ThisSum>MaxSum) MaxSum = ThisSum; } } return MaxSum; }

這個演算法很容易想到,當然該演算法的時間複雜度也是大的可怕,為T(N)=O(N^3)

仔細分析該演算法,發現該演算法做了無用功,當j增加1時,我們沒有必要從頭算起,我們只要在前面i~j的部分和後面加一個元素就行,沒有必要重新算前面i~j部分和,也就是說,k迴圈是多餘的。

那我們優化後的程式碼如下:

優化程式碼

int maxSubseqSum2(int A[],int N)
{
  int i,j;
  int ThisSum,MaxSum = 0;
  for( i = 0; i<N; i++)
  {
    /*i是子列左端位置*/
    ThisSum = 0;/*ThisSum是從A[i]到A[j]的子列和*/
    for( j = i; j<N; j++ )
    {
       /*j是子列右端的位置*/
       ThisSum += A[j];
       if(ThisSum>MaxSum)
       MaxSum = ThisSum;
    }
  }
  return
MaxSum; }

該演算法時間複雜度為T(N) = O(N^2),較第一個演算法快一些。

那還有沒有更快的演算法了呢?答案是肯定的。我們可以應用分而治之的思想去解決這一道題目。
什麼是分而治之呢?參見分而治之

解題思路:把陣列從中間一分為二,然後遞迴地去解決左右兩邊的問題,分別得到左右兩邊的最大子列和,但此時我們還不能下定論,還需要考慮跨越邊界的最大子列和,即得到這三個結果,返回其中的最大值。程式碼如下

分而治之法

int Max(int A, int B, int C)
{
    /*return A > B ? A > C ? A :C: B > C ? B : C;*/
    if (A > B) {
        if (A > C)
            return A;
        else
            return C;
    }
    else if (B > C)
        return B;
    else return C;

}
int DivideAndConquer(int List[], int left, int right) {
    int MaxLeftSum, MaxRightSum;
    int MaxLeftBoardSum, MaxRightBoardSum;
    int LeftBoardSum, RightBoardSum;
    int center,i;
    /*遞迴終止條件*/
    if (left == right) {
        if (List[left] > 0)
            return List[left];
        else
            return 0;
    }


    center = (right + left) / 2;
    MaxLeftSum = DivideAndConquer(List, left, center);
    MaxRightSum= DivideAndConquer(List, center+1, right);

    MaxLeftBoardSum = 0; LeftBoardSum = 0;
    for (i = center;i >= left; i--)
        LeftBoardSum += List[i];
    if (LeftBoardSum > MaxLeftBoardSum)
        MaxLeftBoardSum = LeftBoardSum;
    MaxRightBoardSum = 0; RightBoardSum = 0;

    for (i = center + 1; i <= right; i++)
        RightBoardSum += List[i];
    if (RightBoardSum > MaxRightBoardSum)
        MaxRightBoardSum = RightBoardSum;

    return Max(MaxLeftSum, MaxRightSum, MaxRightBoardSum + MaxLeftBoardSum);


}
int maxsequence3(int A[], int N)
{
    return DivideAndConquer(A, 0, N-1);
}

該演算法的時間複雜度為T(N)=O(N*logN),較之前兩個有很大改進。

總結

把一個大規模的問題,化簡為幾個小問題,分別解決每個小問題,我們便可以輕鬆得到問題的答案。由此可見遞迴的思想的重要性。

最後,祝願所有女性朋友們女神節快樂!

—Alisa 寫於 江蘇泰州 2017.03.08