問題:輸入一個整形數組(有正數也有負數),數組中連續的、一個或多個元素組成一個子數組,每個子數組都有一個和。求所有子數組的和的最大值。
輸入:測試數組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 }