子數組最大和

來源:互聯網
上載者:User

轉自:http://blog.csdn.net/leojames007/article/details/6798123題目:輸入一個整形數組,數組裡有正數也有負數。數組中連續的一個或多個整數組成一個子數組,每個子數組都有一個和。求所有子數組的和的最大值。要求時間複雜度為O(n)。        例如輸入的數組為1, -2, 3, 10, -4, 7, 2, -5,和最大的子數組為3, 10, -4, 7, 2,因此輸出為該子數組的和18。        如果不考慮時間複雜度,我們可以枚舉出所有子數組並求出他們的和。不過非常遺憾的是,由於長度為n的數組有O(n2),具體是n*(n+1)/2個子數組;而且求一個長度為n的數組的和的時間複雜度為O(n)。因此這種思路的時間是O(n3)。 解題思路:很容易理解,當我們加上一個正數時,和會增加;當我們加上一個負數時,和會減少。如果當前得到的和是個負數,那麼這個和在接下來的累加中應該拋棄並重新清零,不然的話這個負數將會減少接下來的和。 代碼: ///////////////////////////////////////////Find the greatest sum of all sub-arrays 
//Return value: if the input is valid, return true, otherwise return false 
/////////////////////////////////////// 
bool FindGreatestSumOfSubArray 

            int *pData,               // an array 
            unsigned int nLength, // the length of array 
            int &nGreatestSum    // the greatest sum of all sub-arrays 


            // if the input is invalid, return false 
            if((pData == NULL) || (nLength == 0)) 
                        return false; 
            int nCurSum = nGreatestSum = 0; 
            for(unsigned int i = 0; i < nLength; ++i) 
            { 
                        nCurSum += pData; 
                        // if the current sum is negative, discard it 
                        if(nCurSum < 0) 
                                    nCurSum = 0; 

                        // if a greater sum is found, update the greatest sum 
                        if(nCurSum > nGreatestSum) 
                                    nGreatestSum = nCurSum; 
            } 
            // if all data are negative, find the greatest element in the array 
            if(nGreatestSum == 0) 
            { 
                        nGreatestSum = pData[0]; 
                        for(unsigned int i = 1; i < nLength; ++i) 
                        { 
                                    if(pData[i] > nGreatestSum) 
                                                nGreatestSum = pData[i]; 
                        } 
            } 

            return true; 
}    討論:·         函數的傳回值不是子數組和的最大值,而是一個判斷輸入是否有效標誌。如果函數傳回值的是子數組和的最大值,那麼當輸入一個null 指標是應該返回什麼呢?返回0?那這個函數的使用者怎麼區分輸入無效和子數組和的最大值剛好是0這兩中情況呢?基於這個考慮,本人認為把子數組和的最大值以引用的方式放到參數列表中,同時讓函數返回一個函數是否正常執行的標誌。
·         輸入有一類特殊情況需要特殊處理。當輸入數組中所有整數都是負數時,子數組和的最大值就是數組中的最大元素。 解法一:最直接的解法當然是窮舉遍曆了,把所有的子數組列出來,然後計算和。複雜度可以簡單的想出來:設定兩個變數i和j為子數組邊界,這兩個變數都要遍曆整個數組,然後還需要一個遊標k,來遍曆整個子數組以求和。所以總的複雜度是O(n^3)。代碼如下: 1 int MaxSubSum(int *A,int n)
 2 {
 3     int max = -INFINITE;
 4     int sum = 0;
 5     for (int i = 0 ; i < n ; i++)
 6     {
 7         for (int j = i ; j < n ; j++)
 8         {
 9             for (int k = i ; k <= j ; k++)
10             {
11                 sum += A[k];
12             }
13             if (sum > max)
14             {
15                 max = sum;
16             }
17         }
18     }
19     return max;
20 }
21   
解法一改進版:仔細琢磨就會發現,其實不需要再使用k去遍曆子數組,因為每次j移動都會產生新的子數組,所以只要在每次j移動時進行一下比較,就不會把最大值漏掉。所以只有i和j移動,複雜度降低到O(n^2)。代碼如下: 1 int MaxSubSum(int *A,int n)
 2 {
 3     int max = -INFINITE;
 4     int sum = 0;
 5     for (int i = 0 ; i < n ; i++)
 6     {
 7         sum = 0;
 8         for (int j = i ; j < n ; j++)
 9         {
10             sum += A[j];
11             if (sum > max)
12                 max = sum;
13         }
14     }
15     return max;
16 }
17 
解法二:分治演算法跟二分尋找的思想相似,我們可以分情況討論這個問題是不是符合二分尋找的條件。情況1.這個滿足最大和的子數組全部在本數組的左半部或者右半部。例如:左半部A[i]……A[n/2-1]或者右半部A[n/2]……A[j]。這種情況下可以直接使用遞迴調用。情況2.滿足最大和的子數組跨過了本數組的中間點。例如:A[i]……A[n/2-1] A[n/2]……A[j]連續。則這種情況下只要在左半部尋找以A[n/2-1]結尾,在右半部尋找以A[n/2]開頭的兩個滿足最大和的連續數組,並求和即可。由於這個已知起點,只需要一個遊標即可,所以複雜度是2*O(n/2)=O(n)。綜合以上兩種情況,滿足分治演算法遞迴式:T(n)=2T(n/2)+O(n)=O(n*logn)。代碼如下:
 1 int MaxSubSum(int *A,int Left,int Right)
 2 {
 3     //
 4     int MaxLeftSum,MaxRightSum;
 5     //左右子數組的和的最大值
 6     int MaxLeftPartSum,MaxRightPartSum;
 7     //臨時變數,用於儲存計算出來的和
 8     int LeftPartSum,RightPartSum;
 9     int Center,i; 
10 
11     //其中某一部分只有一個元素
12     if(Left == Right)
13     {
14         if(A[Left] > 0)   
15             return A[Left];   
16         else   
17             return 0;
18     }
19 
20     //遞迴調用。分別計算左右子數組的最大和子數組。
21     //即假設最大和子數組沒有被Center切割
22     Center = (Left+Right)/2;   
23     MaxLeftSum = MaxSubSum(A,Left,Center);   
24     MaxRightSum = MaxSubSum(A,Center+1,Right);   
25 
26     //假設最大和子數組被Center切開的情況
27     //那麼需要從Center開始向兩側計算
28     MaxLeftPartSum = 0;   
29     LeftPartSum = 0;   
30     for(i = Center ; i >= Left; --i )   
31     {   
32         LeftPartSum += A[i];   
33         if(LeftPartSum > MaxLeftPartSum)   
34             MaxLeftPartSum = LeftPartSum;   
35     }   
36     MaxRightPartSum = 0;   
37     RightPartSum = 0;   
38     for(i = Center+1 ; i <= Right ; ++i)   
39     {   
40         RightPartSum += A[i];   
41         if(RightPartSum > MaxRightPartSum)   
42             MaxRightPartSum = RightPartSum;   
43     }
44     //返回三者中的最大值。
45     return max(max(MaxLeftSum,MaxRightSum),MaxLeftPartSum+MaxRightPartSum);   
46 } 
47 
解法三:我們試著再觀察這個數組的特點,一個元素一個元素的看。根據A[0]是否在這個滿足最大和的子數組中,我們可以分為兩種情況。1.       在。那麼可以從A[0]開始求(比較容易)。2.       不在。那麼這種情況,又可以繼續分為兩種情況:A[1]在不在這個滿足最大和的子數組中。從這裡我們可以觀察出一種遞迴的特點,可以把一個規模為N的問題轉化為規模為N-1的問題。所以這個從A[0]到A[n-1]的最大和子數組問題分解成:1.       所求子數組中包含A[0]。如果不包含A[1],則A[0]自己滿足條件,此時Max(A[0]……A[n-1])=A[0]。如果包含A[1],則Max(A[0]……A[n-1])=A[0]+Max(A[1]……A[n-1])。2.       所求子數組中不包含A[0]。Max(A[0]……A[n-1])=Max(A[1]……A[n-1])。最終結果取以上三者的最大值即可,即Max(A[0]……A[n-1])=max( A[0], A[0]+Max(A[1]……A[n-1]), Max(A[1]……A[n-1]))。這個的複雜度為線性:因為只要把數組遍曆一遍即可。代碼如下: 1 int MaxSubSum(int *A,int n)
 2 {
 3     //假設滿足最大和的子數組就是從StartFrom[i]開始
 4     int *StartFrom = new int[n];
 5     memset(StartFrom,n,0);
 6     StartFrom[n-1] = A[n-1];
 7     //假設A[i]之後滿足最大和的子數組的和為Longest[i](不一定包括A[i])
 8     int *Longest = new int[n];
 9     memset(Longest,n,0);
10     Longest[n-1] = A[n-1];
11 
12     for (int i = n-2 ; i >= 0 ; i--)
13     {
14         //如果從i開始,那麼要麼最大和只包括A[i]自己,要麼就是後面的那個序列連上A[i]
15         StartFrom[i] = max(A[i],A[i]+StartFrom[i+1]);
16         //最大和,要麼是從i開始的,要麼還是以前的
17         Longest[i] = max(StartFrom[i],Longest[i+1]);
18     }
19     //最後結果是在號元素中儲存
20     return Longest[0];
21 }
22 
由於這種前後單元素的相關性,實際上不需要兩個數組來儲存這個資訊,只需要兩個變數即可,這樣可以減小程式的空間複雜度。代碼如下:  1 int MaxSubSum(int *A,int n)
 2 {
 3     //假設滿足最大和的子數組就是從StartFrom開始
 4     int StartFrom = A[n-1];
 5     //假設A[i]之後滿足最大和的子數組的和為Longest(不一定包括A[i])
 6     int Longest = A[n-1];
 7 
 8     for (int i = n-2 ; i >= 0 ; i--)
 9     {
10         //如果從i開始,那麼要麼最大和只包括A[i]自己,要麼就是後面的那個序列連上A[i]
11         StartFrom = max(A[i],A[i]+StartFrom);
12         //最大和,要麼是從i開始的,要麼還是以前的
13         Longest = max(StartFrom,Longest);
14     }
15     //最後結果是在0號元素中儲存
16     return Longest;
17 }
 輸出子序列的起點和終點,並輸出該子序列的和#include <stdio.h> 
#include <stdlib.h> 
#include <limits.h> 
#include <malloc.h> 

int main()


    int *ip; 
    int j,length,max,sum; 
    int start1 = 0 ,start2 = 0; 
    
    printf("Please enter the array's length:"); 
    scanf("%d",&length); 
    if((ip = (int*)malloc(length*sizeof(int)))==NULL) 
    { 
            fprintf(stderr,"Malloc memory failed !"); 
            exit(1); 
    } 
    printf("Enter eath element:"); 
    for(j = 0; j < length ; j ++) 
    scanf("%d",ip+j); 

    max = INT_MIN; 
    for(sum = j = 0; j < length; j ++) 
    { 
            sum += *(ip+j); 
            if(max < sum) 
            { 
                    start1 = start2; 
                    max = sum; 
            } 
            if(sum < 0){ 
                    start2 = j+1; 
                    sum = 0; 
            } 
     } 
     for(j = start1,sum = 0; sum != max; j ++) 
            sum += *(ip+j); 
     printf("\nThe subsequence from %d to %d,max sum is %d\n",start1,j-1,max); 
  return 0; 
}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.