分治法解決最大子數組問題


問題:輸入一個整形數組(有正數也有負數),數組中連續的、一個或多個元素組成一個子數組,每個子數組都有一個和。求所有子數組的和的最大值。

輸入:測試數組1, -2, 3, 10, -4, 7, 2, -5;

輸出:最大子數組為3, 10, -4, 7, 2;

   輸出最大子數組的和為18 。

1.蠻力法求解

總體思路:

  蠻力法是最簡單的實現方法,只要列出數組所有可能的組合,然后找出其中和最大的組合即可;

  蠻力法分三層循環實現:

    1)第一層循環用於固定子數組的起始位置;

    2)第二層循環用於確定子數組的結束位置;

    3)第三層循環用於子數組和的計算,從子數組的頭開始遍歷到其尾,累加起來就是該子數組的和。

代碼實現:

 1 int ForseMax(int *arry,int n,int &_start,int &_end)
 2 {
 3     int i,j,k;
 4     int sum;//用於求和
 5     int _max=MIN;//記錄最大和
 6     for(i=0;i<n;i++)
 7     {
 8         for(j=i;j<n;j++)
 9         {
10             sum=0;
11             for(k=i;k<j;k++)
12             {
13                 sum+=arry[k];
14             }
15             if(sum>_max)
16             {
17                 _start=i;//子數組開始處
18                 _end=j;//子數組結尾處
19                 _max=sum;
20             }
21         }
22     }
23     return _max;//返回最大和
24 }

2.分治法求解

總體思路:

  分治法的精髓:
    1)分--將問題分解為規模更小的子問題;
    2)治--將這些規模更小的子問題逐個擊破;
    3)合--將已解決的子問題合並,最終得出“母”問題的解;
  所以原數組的最大子數組求法:
    1)分--將原數組拆分成兩部分,每個部分再拆分成新的兩部分......直到數組被分得只剩下一個元素;
    2)治--每個小型的數組找最大子數組,只有一個元素的數組,解就是該元素;
    3)合--將兩個小型數組合並為一個數組,其中解有三種可能:
      • 左邊的返回值大,
      • 右邊的返回值大,
      • 中間存在一個更大的子數組和;
     返回值應選最大的;

模塊實現:

 1 int Divide(int *arry,int l,int r)
 2 {
 3     if(l==r)//只有一個元素時,返回該元素
 4         return arry[l];
 5     else
 6     {
 7         int m=(l+r)/2;
 8         int l_max=MIN,r_max=MIN,m_max=MIN;
 9         l_max=Divide(arry,l,m);//左邊和的最大值
10         r_max=Divide(arry,m+1,r);//右邊和的最大值
11         m_max=MiddleMax(arry,l,r,m);//中間和的最大值
12         //返回三個值中最大的一個
13         if(l_max>=r_max && l_max>=m_max)
14             return l_max;
15         else if(r_max>=l_max && r_max>=m_max)
16             return r_max;
17         else
18             return m_max;
19     }
20 }

難點解說:

  其中難點在於兩個數組合並的時候,位於兩個數組中間位置存在最大和的情況,處理方法為:

從中間位置開始,分別向左和向右兩個方向進行操作,通過累加找到兩個方向的最大和,分別為l_max和r_max,因此存在於中間的最大和為(l_max+r_max);

  向左的累加操作和向右的累加操作完全一樣,只需要一層循環就可以解決問題:

  1)初始化l_max、r_max為最小值,命sum=0用於累加;

  2)在向左累加的操作中,sum從中點開始向左逐個累加,累加完一個元素后與l_max相比,l_max保留值較大的一個;

  3)等遍歷完左邊部分l_max的值得以確認,並用同樣的方法確認r_max的值;

  4)最后返回(l_max+r_max)的值。

  具體代碼實現如下:

 1 int MiddleMax(int *arry,int l,int r,int m)
 2 {
 3     int l_max=MIN,r_max=MIN;//分別用於記錄左、右方向累加的最大和
 4     int i;
 5     int sum;//用於求和
 6     sum=0;
 7     for(i=m;i>=l;i--)//中線開始向左尋找
 8     {
 9         sum+=arry[i];
10         if(sum>l_max)
11             l_max=sum;
12     }
13     sum=0;
14     for(i=m+1;i<r;i++)//中線開始向右尋找
15     {
16         sum+=arry[i];
17         if(sum>r_max)
18             r_max=sum;
19     }
20     return (l_max+r_max);//返回左右之和
21 }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM