1. 程式人生 > 實用技巧 >letcode每日一題-不同路徑

letcode每日一題-不同路徑

話不多說,我們上題目:

方法一:動態規劃

我們用 f(i, j)表示從左上角走到 (i, j) 的路徑數量,其中 i 和 j 的範圍分別是 [0, m)和 [0, n)。

由於我們每一步只能從向下或者向右移動一步,因此要想走到 (i, j),如果向下走一步,那麼會從 (i−1,j) 走過來;
如果向右走一步,那麼會從 (i, j-1)走過來。因此我們可以寫出動態規劃轉移方程:

f(i, j) = f(i-1, j) + f(i, j-1)

需要注意的是,如果 i=0,那麼 f(i-1,j)並不是一個滿足要求的狀態,我們需要忽略這一項;同理,如果 j=0,那麼
f(i,j-1) 並不是一個滿足要求的狀態,我們需要忽略這一項。

初始條件為 f(0,0)=1,即從左上角走到左上角有一種方法。

最終的答案即為 f(m-1,n-1)。
程式碼實現如下:


public int uniquePaths(int m, int n) {
        int[][] tmpResult=new int[m][n];
        for(int i=0;i<n;i++){
            tmpResult[0][i]=1;
        }
        for(int i=0;i<m;i++){
            tmpResult[i][0]=1;
        }
        for(int i=1;i<m;i++){
            for(int j=1;j<n;j++){
                tmpResult[i][j] = tmpResult[i - 1][j] + tmpResult[i][j - 1];
            }
        }
        return tmpResult[m-1][n-1];
    }

方法二: 遞迴
遞迴則是從“Finish”出發,往上走,假設往上的位置是i,往左的位置是j,則有:
1.i>1且j>1 則有兩條路線,繼續往上找就好
2.i>1 或 j>1 則說明到了邊緣,只有一種路線了,返回1就可以
3.i=1且j=1 說明是終點了,無路線了,返回0即可
程式碼實現如下:

public int uniquePaths(int m, int n) {
        if(m==1 && n==1){
            return 1;
        }
        return bianli(m,n);
    }

    public int  bianli(int m, int n){
        if(m>1 && n>1){
            return bianli(m-1,n)+bianli(m,n-1);
        }else if(n>1 || m>1){
            return 1;
        }else {
            return 0;
        }
    }

雖然遞迴運算的結果和動態規劃的結果相同,但是遞迴的在執行的時候卻超時了,瞭解了一下,記錄下
原因:
大家都知道遞迴的實現是通過呼叫函式本身,函式呼叫的時候,每次呼叫時要做地址儲存,引數傳遞等,這是通過
一個遞迴工作棧實現的。具體是每次呼叫函式本身要儲存的內容包括:區域性變數、形參、呼叫函式地址、返回值。
那麼,如果遞迴呼叫N次,就要分配N區域性變數、N形參、N呼叫函式地址、N返回值,這勢必是影響效率的,同時,
這也是記憶體溢位的原因,因為積累了大量的中間變數無法釋放。
1.1用迴圈效率會比遞迴效率高嗎?

遞迴與迴圈是兩種不同的解決問題的典型思路。當然也並不是說迴圈效率就一定比遞迴高,遞迴和迴圈是兩碼事,
遞迴帶有棧操作,迴圈則不一定,兩個概念不是一個層次,不同場景做不同的嘗試。

2.1遞迴演算法:

優點:程式碼簡潔、清晰,並且容易驗證正確性。(如果你真的理解了演算法的話,否則你更暈)

缺點:它的執行需要較多次數的函式呼叫,如果呼叫層數比較深,需要增加額外的堆疊處理(還有可能出現堆疊溢
出的情況),比如引數傳遞需要壓棧等操作,會對執行效率有一定影響。但是,對於某些問題,如果不使用遞迴,
那將是極端難看的程式碼。

2.2迴圈演算法:

優點:速度快,結構簡單。

缺點:並不能解決所有的問題。有的問題適合使用遞迴而不是迴圈。如果使用迴圈並不困難的話,最好使用迴圈。

2.3遞迴演算法和迴圈演算法總結:

1) 一般遞迴呼叫可以處理的演算法,也可以通過迴圈去解決,常需要額外的低效處理。

2)現在的編譯器在優化後,對於多次呼叫的函式處理會有非常好的效率優化,效率未必低於迴圈。

3) 遞迴和迴圈兩者完全可以互換。如果用到遞迴的地方可以很方便使用迴圈替換,而不影響程式的閱讀,那麼替
換成迴圈往往是好的。(例如:求階乘的遞迴實現與迴圈實現。)