分而治之的思想--最大子列和問題
阿新 • • 發佈:2019-02-05
問題描述:給定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