最大子列和(時間複雜度)程式碼實現及結果對比
阿新 • • 發佈:2018-12-14
題目:給定N個整數的序列{}, 求函式的最大值
這一部分是浙大資料結構第一章節的一道題目,主要目的是講解時間複雜度。
方法一
- 直接使用迴圈遍歷的方法。
int MaxSubseqSum1( int A[], int N)
{
int ThisSum, MaxSum = 0;
int i,j,k;
for (i=0; i<N, i++){//左邊最小值
for (j=i; j<N; j++){//右邊最小值
ThisSum = 0;
for (k=i; k<=j; k++)//中間全部加和
ThisSum +=A[k];
if(ThisSum > MaxSum)//如果大於最大值
MaxSum = ThisSum;//替換
}
}
}
- 這種方法比較暴力,需要全部遍歷,因為嵌套了3層迴圈,所以時間複雜度為
方法二
- 第一步在進行k迴圈時很顯然是沒有必要的,因為每次增加一個j的時候,只需要在上一步ThisSum上加一個就可以了。
int MaxSubseqSum1( int A[], int N)
{
int ThisSum, MaxSum = 0;
int i,j,k;
for (i=0; i<N, i++){
for (j=i; j<N; j++){
ThisSum += A[j];//在上一個的基礎上直接加一個A[j]就可以了
if(ThisSum > MaxSum)
MaxSum = ThisSum;
}
}
}
- 這種方法只嵌套了兩個迴圈,所以時間複雜度為.
方法三:分而治之
前兩種方法都比較好理解,這種分而治之的方法就有一定的難度了。這個問題的本質是一個遞迴的問題。
把一個複雜的問題分成兩個或更多的相同或相似的子問題,再把子問題分成更小的子問題……直到最後子問題可以簡單的直接求解,原問題的解即子問題的解的合併.
分治法在每一層遞迴上都有三個步驟:
-
分解:將原問題分解為若干個規模較小,相互獨立,與原問題形式相同的子問題;
-
解決:若子問題規模較小而容易被解決則直接解,否則遞迴地解各個子問題
-
合併:將各個子問題的解合併為原問題的解。
對於這一個問題,可以簡化先進行分析:
- 首先,假設陣列中有8個元素,如上圖所示,我們先沿紅色的線從中間分開,得到兩部分,發現可以進一步的劃分,直到使用黃色的線劃分完畢。
- 首先看4,-3,我們可以看出最大的值為4,這裡我們記下最大值為4,然後看5,-2,記下最大值5.同樣的道理可以得到最大值2,6。
- 下一步,我們來看跨越分割線的最大值,首先是4,-3,5,-2這4個數。從-3開始向右,得到最大的值要加到4,然後向左,得到最大的值為5,這樣得到跨越邊界的最大值為6,比較紅線右側得到的最大值,得到最大值為6。同理我們可以得到左側的最大值為8. 4.同理繼續向下,跨越紅色的最大值為11,這樣得到所有子空間的最大值為11。
以上就是使用分而治之的思想得到的最大值的結果。
這一部分的程式碼見完整程式碼部分。
方法四:線上處理
線上處理的方法是這些的方法中時間複雜度最小的演算法。就我的理解寫一些。 這種演算法只需要遍歷一遍陣列,其核心的思想是置零的那一部分。分為
- 首先從第一個非零數字開始,向後加和。
- 在加的過程中不斷的判斷是否為最大值,將最大值儲存。
- 直到遇到子列和負值,將已經得到的最大值儲存,然後結束這一部分,直到遇到下一個正數再重新進行第1步。
帶入到上圖中的例子,第一個數為-1,直接舍掉,第二個為正3,最大值記為3,加上第三個數-2,為1小於3,最大值仍然為3,加上4,最大值變為5,大於3,此時最大值變為5.因為加上下一個數之後小於零,所以這一部分計算結束,這部分子空間的最大值為5.從下一個1開始,可以得到最大值為7,所以最大的子空間的加和為7.
int MaxSubseqSum4( intA[], int N)
{
int ThisSum, MaxSum;
int i;
ThisSum = MaxSum = 0;
for (i=0; i<N; i++){
ThisSum += A[i];
if(ThisSum > MaxSum)
MaxSum = ThisSum;
else if(ThisSum < 0);//子列和為負,結束該子列
ThisSum = 0
}
return MaxSum;
}
這種方法只使用了一次迴圈,所以時間複雜度為,已經是時間複雜度最小的方法。
完整程式碼及對比
#include <stdio.h>
#include <time.h>
#include <math.h>
clock_t start, stop;
double duration;
int MaxSubseqSum1( int A[], int N)
{
int ThisSum, MaxSum = 0;
int i,j,k;
for (i=0; i<N; i++){
for (j=i; j<N; j++){
ThisSum = 0;
for (k=i; k<=j; k++)
ThisSum +=A[k];
if(ThisSum > MaxSum)
MaxSum = ThisSum;
}
}
return MaxSum;
}
int MaxSubseqSum2( int A[], int N)
{
int ThisSum, MaxSum = 0;
int i,j,k;
for (i=0; i<N; i++){
for (j=i; j<N; j++){
ThisSum +=A[j];
if(ThisSum > MaxSum)
MaxSum = ThisSum;
}
ThisSum = 0;
}
return MaxSum;
}
int Max3( int A, int B, int C )
{ /* 返回3個整數中的最大值 */
return A > B ? A > C ? A : C : B > C ? B : C;
}
int DivideAndConquer( int List[], int left, int right )
{ /* 分治法求List[left]到List[right]的最大子列和 */
int MaxLeftSum, MaxRightSum; /* 存放左右子問題的解 */
int MaxLeftBorderSum, MaxRightBorderSum; /*存放跨分界線的結果*/
int LeftBorderSum, RightBorderSum;
int center, i;
if( left == right ) { /* 遞迴的終止條件,子列只有1個數字 */
if( List[left] > 0 ) return List[left];
else return 0;
}
/* 下面是"分"的過程 */
center = ( left + right ) / 2; /* 找到中分點 */
/* 遞迴求得兩邊子列的最大和 */
MaxLeftSum = DivideAndConquer( List, left, center );
MaxRightSum = DivideAndConquer( List, center+1, right );
/* 下面求跨分界線的最大子列和 */
MaxLeftBorderSum = 0;
LeftBorderSum = 0;
for( i=center; i>=left; i-- ) { /* 從中線向左掃描 */
LeftBorderSum += List[i];
if( LeftBorderSum > MaxLeftBorderSum )
MaxLeftBorderSum = LeftBorderSum;
} /* 左邊掃描結束 */
MaxRightBorderSum = 0; RightBorderSum = 0;
for( i=center+1; i<=right; i++ ) { /* 從中線向右掃描 */
RightBorderSum += List[i];
if( RightBorderSum > MaxRightBorderSum )
MaxRightBorderSum = RightBorderSum;
} /* 右邊掃描結束 */
/* 下面返回"治"的結果 */
return Max3( MaxLeftSum, MaxRightSum, MaxLeftBorderSum + MaxRightBorderSum );
}
int MaxSubseqSum3( int A[], int N )
{ /* 保持與前2種演算法相同的函式介面 */
return DivideAndConquer( A, 0, N-1 );
}
int MaxSubseqSum4( int A[], int N)
{
int ThisSum, MaxSum;
int i;
ThisSum = MaxSum = 0;
for (i=0; i<N; i++){
ThisSum += A[i];
if(ThisSum > MaxSum)
MaxSum = ThisSum;
else if(ThisSum < 0)//子列和為負,結束該子列
ThisSum = 0;
}
return MaxSum;
}
int main(){
int N = 8;
int max1, max2, max3, max4;
int A[8] = {-1, 3, -2, 4, -6, 1, 6, -1};
start = clock();
max1 = MaxSubseqSum1(A, N);
stop = clock();
duration = ((double)(stop-start))/CLOCKS_PER_SEC;
printf("Max1 = %d\n",max1);
printf("duration1 = %6.2e\n",duration);
start = clock();
max2 = MaxSubseqSum2(A, N);
stop = clock();
duration = ((double)(stop-start))/CLOCKS_PER_SEC;
printf("Max2 = %d\n",max2);
printf("duration2 = %6.2e\n",duration);
start = clock();
max3 = MaxSubseqSum3(A, N);
stop = clock();
duration = ((double)(stop-start))/CLOCKS_PER_SEC;
printf("Max3 = %d\n",max3);
printf("duration3 = %6.2e\n",duration);
start = clock();
max4 = MaxSubseqSum4(A, N);
stop = clock();
duration = ((double)(stop-start))/CLOCKS_PER_SEC;
printf("Max4 = %d\n",max4);
printf("duration4 = %6.2e\n",duration);
}
- 再貼一下執行的結果,其實已經可以看出來速度的差距了
Max1 = 7
duration1 = 3.00e-06
Max2 = 7
duration2 = 2.00e-06
Max3 = 7
duration3 = 2.00e-06
Max4 = 7
duration4 = 1.00e-06
[Finished in 2.1s]