深入 最大子串行和(多種演算法)

2021-08-04 19:03:01 字數 4090 閱讀 6818

給定乙個整數序列,a0, a1, a2, …… , an(項可以為負數),求其中最大的子串行和。如果所有整數都是負數,那麼最大子串行和為0;

例如:對於序列-2, 11, -4, 13, -5, –2。 所求的最大子串行和為20(從11到13,即從a1到a3)。

用於測試下面**的的主函式**如下:(注意要更改呼叫的函式名)

[cpp]view plain

copy

intmain(

intargc, 

char

**argv)    

這是最容易想到的,也是最直接的,就是對所有的子串行求和,**如下:

[cpp]view plain

copy

intmaxsubsum1( 

const

vector<

int> &a )  

if(thissum>maxsum)  

}  }  return

maxsum;  

}  

分析:

① 三層迴圈巢狀,時間複雜度為o(n^3);

② 包含有大量的重複計算,例如i=1時: 當 j=3,則計算a1+a2+a3;j=4,則計算a1+a2+a3+a4;其中a1+a2+a3是重複計算的。

另一種思路:上面的方法在每一次迴圈中,固定i,並把a[i]當做起點,下面的方法將a[i]當做終點。

[cpp]view plain

copy

intmaxsubsum1_2( 

const

vector<

int> &a )  

}  }  }  

return

maxsum;   

}  

如果你意識到,子串行總要有乙個位置開始,那麼變換一下迴圈方式,只要求出在所有位置開始的子串行,找到最大的。**如下:

[cpp]view plain

copy

intmaxsubsum2( 

const

vector<

int> &a )  

}  }  return

maxsum;  

}  

分析:

① 兩層迴圈巢狀,時間複雜度為o(n^2);

② 雖然比方法一要少,但同樣包含重複計算。例如:當i=1時,要計算a1+a2+a3+a4+……;當i=2時,要計算a2+a3+a4+……;其中a2+a3+a4+……是重複的。

注意:下面是乙個錯誤的方法,因為它的起始點固定了,每次都從a0開始,是不能保證遍歷所有的子串行的。

[cpp]view plain

copy

intmaxsubsum2_2( 

const

vector<

int> &a )  

}  }  return

maxsum;  

}  

如果希望將固定終點,那麼計算的時候就要從終點開始,依次往前累加。**如下:

[cpp]view plain

copy

intmaxsubsum2_3( 

const

vector<

int> &a )  

}  }  return

maxsum;  

}  

到目前為止,題目的資訊你只用到了:「最小子序列之和為0」(若有一項大於0,那麼子串行的和一定大於或等於該項,也就大於0;因為若所有項都是負數,那麼結果為0

如果你再挖掘一下題意:你就會發現,如果a[i]是負的,那麼a[i]一定不是最終所有結果子串行的起始點。**可以改造為:

[cpp]view plain

copy

intmaxsubsum3_1( 

const

vector<

int> &a )  

}  }  return

maxsum;  

}  

如果你又再一步發現:任何負的子串行,不可能作為最優子串行的字首。

又因為上一步已經保證,序列以正數開頭a[i]>0,所以若a[i]到a[j]之間元素的序列和 thissum<=0時,則i+1和j之間元素不會為最優子串行的字首,可以讓i=j,即不需要判斷在i和j之間元素開頭。**如下

[cpp]view plain

copy

intmaxsubsum3_2( 

const

vector<

int> &a )  

else

if( thissum <= 0 )  

}  }  

return

maxsum;  

}  

如果你又進一步發現:因為要求序列開始元素大於0, 若以a[i]開頭的序列,a[i]>0,那麼可以知道,所求的最終子串行一定不會以a[i+1]開始, 因為若到相同的元素終止,那麼從a[i]開始序列,一定大於從a[i+1]開始的序列。因為s[i, k]=s[i+1, k]+a[i] 例如:a1+a2+a3>0,而又由於這時a1>0, 那麼所求子串行一定不會以a2開始,因為從a1開始會更大。 更進一步,如若乙個子串行thissum>0(其中thissum是從第m項到第n項的和),那麼序列一定不會以a[m]和a[n]之間的項開始。 因為一直thissum第乙個元素a[m]是大於0的,且以a[m]開始的所有子串行都是大於0的,因為若存在子串行小於0,就會提前返回了。 例如:若程式執行到thissum (為a2+a3+a4),若thissum>0,則能說明,a2>0,並且a2+a3>0, a2+a3+a4>0。那麼 也可以跳過a[m]和a[n]之間的項,即另 i=j。

[cpp]view plain

copy

intmaxsubsum3_3( 

const

vector<

int> &a )  

i = j; //(相對方法3_2 新添)

}  else

if( thissum <= 0 )  

}  }  return

maxsum;  

}  

最後如果你又了解一些程式結構上的優化的知識,那麼你會發現下面的問題:

① 迴圈的分支可以改變一下,去除巢狀分支結構。

② 判斷語句的分支中有共同部分,( i=j ),可以抽取出來。

以上兩步以後,迴圈部分的**程式設計 變成:

[cpp]view plain

copy

for(

inti=0; i

else

if( thissum>0 )   

else

if( thissum <= 0 )   

i = j;   

}   

}  

③ 下面這步非常重要,如果你發現,內層迴圈的迴圈變數j 和 外層迴圈的迴圈變數i

同步增長,那麼你是否能夠想到,外層迴圈可能沒有存在的必要。在這裡到底能不能去除外層迴圈,取決於外層迴圈中是否有額外的工作要做。這裡的額外工作是是if(a[i] <=0) 判斷語句,如果你能發現內層迴圈的 if(thissum < 0)的判斷能夠替代 if( a[i]<=0 ) 的工作。因為thissum是由a[j]得到的。

到這裡,**就可以神奇的變為如下的形式:常量空間,線性時間

[cpp]view plain

copy

intmaxsubsum3_4( 

const

vector<

int> &a )   

else

if( thissum>0 )   

else

if( thissum < 0 )   

}   

return

maxsum;   

}  

分析:

① 只有一層迴圈,時間複雜度為o(n)

。常量空間,線性時間,這是最優解法

最大子串行和演算法

include stdio.h 演算法1 int maxsubsequencesum1 const int a,int n int thissum,maxsum,i,j,k maxsum 0 for i 0 i n i for j i j n j thissum 0 for k i k j k th...

最大子串行和演算法

題目 求a i 中和最大的子串行。時間複雜度o nlogn 使用分治 遞迴的方法。分別求出左邊n 2長度的最大子串行和 右邊n 2長度的最打字序列和 以及橫跨這兩部分且通過中間的最大和 要分別求出兩邊帶有最中間邊界的最子大序列和,將其相加 由於不是最簡單的方法,這裡不再贅述。時間複雜度o n 遍歷一...

演算法 最大子串行和

最大子串行和的問題,資料結構與演算法一書分析中給出了四種演算法,最優的演算法的時間複雜度為o n 1 定義控制台應用程式的入口點。2 34 include stdafx.h 5 include 6 using namespace std 7int maxsubsequesum const int a...