1. 程式人生 > >構建乘積數組--java

構建乘積數組--java

找到 ext 復雜 分組 multi 優化 n-2 這樣的 span

題目:給定一個數組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表示成矩陣的形式如下:

 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)。

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)。

構建乘積數組--java