題目:給定一個數組A[0,1,...,n-1],請構建一個數組B[0,1,...,n-1],其中B中的元素B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。不能使用除法。算法
解析:這道題,直觀的解法是:設置一個循環(由0到n-1),計算B[i]時,忽略掉A[i]項,把數組A中的其餘項所有相乘,即獲得B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。這樣,一次循環事後,就能夠在沒有除法的條件下,獲得數組B中全部的值。那麼,這種解法的時間複雜度和空間複雜度是多少呢?時間複雜度:因爲循環由0~n-1,即o(n),在每次循環中,要執行n-1次乘法運算,因此這種解法的時間複雜度爲o(n)*o(n-1)=o(n2);空間複雜度爲o(1)。數組
顯然,上面這種解法的時間複雜度較高,那麼有沒有o(n)的解法呢?咱們不妨分析一下上面這種解法的問題,從而找到能夠優化的突破口。上面的複雜度由兩部分組成,第一部分:循環由0~n-1,顯然咱們須要計算每個B[i],無論怎樣,咱們都沒辦法去掉這樣的基本循環,也就是說這一部分帶來了時間複雜度o(n)不能再進行優化;第二部分:每一次計算B[i]都須要進行n-1次乘法,也便是須要計算n次n-1個數相乘,細心的人能夠發現,這裏面有不少的乘法是重複的,正是因爲這部分重複的乘法計算形成咱們的時間複雜度很高。那麼,有沒有辦法只計算一次這樣的n-1個數相乘呢?咱們能夠定義兩個中間數組來存儲已經計算過得乘法結果,這樣,在進行下一個B[i]計算時,咱們只須要完成一次乘法就能夠獲得B[i]的結果了。這樣,時間複雜度就變成了o(1),整個算法時間複雜度就降成了o(n)。具體分析思路以下:優化
首先,咱們能夠將數組B表示成矩陣的形式以下:spa
B[0] | 1 | A[1] | A[2] | ... | A[n-2] | A[n-1] |
B[1] | A[0] | 1 | A[2] | ... | A[n-2] | A[n-1] |
B[2] | A[0] | A[1] | 1 | ... | A[n-2] | A[n-1] |
... | ... | ... | ... | 1 | ... | ... |
B[n-2] | A[0] | A[1] | A[2] | ... | 1 | A[n-1] |
B[n-1] | A[0] | A[1] | A[2] | ... | A[n-2] | 1 |
如上圖所示,矩陣的每一行表明數組B的一個元素,從上往下,依次是B[0],B[1],...B[n-2],B[n-1]。那麼,咱們能夠將每個B[i]當作兩部分,分別用C[i]和D[i]表示。其中C[i] = A[0]*A[1]*...*A[i-1],D[i] = A[i+1]*...*A[n-2]*A[n-1],這樣,B[i]=C[i]*1*D[i]。也就是說,咱們能夠每次更新數組C[i]和D[i],即C[i]=C[i-1]*A[i-1]和D[i]=D[i+1]*A[i+1](須要說明的是,這裏C[i]的更新是按照從前日後,而D[i]則是從後往前),從而爲咱們節省了大量的重複的乘法計算,使得時間複雜度降爲o(n)。code
int[] multiply(int[] A){ if(A==null||A.length<=0)//邊界條件,最好附帶上 return null; int n = A.length; int[] B = new int[n]; B[0]=1; /*更新C[i],這裏咱們不另外定義數組,直接將C[i]的計算結果存儲在B[i]中, 這樣,再將D[i]的結果直接乘以B[i](此時B[i]等於C[i]),就獲得了最終 的B[i],顯然爲咱們又節省了很多空間存儲。 */ for(int i=1;i<n;i++){ B[i]=B[i-1]*A[i-1]; } int temp =1; //更新D[i],這裏要從n-2開始,由於B[n-1]已獲得最終結果 for(int j=n-2;j>=0;j--){ temp*=A[j+1]; B[j]*=temp; }
return B; }
顯然,上述解法的時間複雜度爲:2*o(n)*2=o(n)。blog