遞歸原來可以so easy|-連載(4)

對遞歸進行優化--記憶化

遞歸可以很方便的解決很多問題,讓程序變得很簡潔。

但是,在遞歸解決問題的過程成,有時候會有很多重複計算,使得計算量很大,耗時很長。

比如,使用遞歸求斐波那契數列。

如果用普通的遞歸來解,當n值很大時,時間會很長而超時。

遞歸原來可以so easy|-連載(4)

如圖,當n等於45時,需要運行5秒才能求出結果。

分析一下,會是什麼原因導致需要計算這麼長時間呢?

根據斐波那契數列的遞推公式:

fn=f(n-1)+f(n-2)

f(n-1)=f(n-2)+f(n-3)
f(n-2)=f(n-3)+f(n-4);
以上3行合併一下:
fn=( f(n-2)+f(n-3) ) + ( f(n-3)+f(n-4) )

可以看到, f(n-3)被重複計算了。

再看一下斐波那契數列的運行時圖:
遞歸原來可以so easy|-連載(4)

可以看到,有大量的重複計算。

有沒有什麼辦法可以優化呢?

如果我們能記錄一些狀態的中間結果,在需要的時候直接讀取結果,就可以減少重複計算。

這個思想叫着記憶化。

對於斐波那契數列,我們可以把計算完 n=2,n=3時...的值保存在數組中,下次需要使用他們來計算其他n值時,從數組中取出來,而不去再計算。

按照這個思想,代碼修改爲:
遞歸原來可以so easy|-連載(4)

可以看到,經過記憶化優化後的程序,運行速度快了很多。

下面我們再看個例子。

數字三角形問題

5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
如上圖所示,第一行有一個數字5,表示數字三角形共有5層。

後面5行爲各層的數字,組成一個數字三角形。

給出一個三角形,從三角形的頂點,只能走左下方或者右下方,然後把所遍歷的數字加起來,求出最大的值。
比如對於上圖來說,答案就是30(7+3+8+7+5)。

現給定一個任意的數字三角形,輸出最大的值。

我們從頂點(也就是第一行第一列)開始走,其最大值爲頂點的值加上從他下面兩個數開始,走到底邊的最大值中,更大的那個,由此,我們把問題分解成求第一行的數字到底邊的最大值,到求第二行的數字到底邊的最大值,這就形成了遞歸。
而遞歸的終止條件是到了最後一行,其值就是它本身。
遞歸的代碼如下:

package test;

import java.util.Scanner;

public class Hello {
    static int d[][] = new int[100][100]; // 最多100層
    static int n;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt(); // 總共有幾層
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= i; j++)
                d[i][j] = sc.nextInt();
        int m = MaxSum(1, 1);
        System.out.println("最大值:"+m);
    }

    static int MaxSum(int l, int r) {
        if (l == n)
            return d[l][r];
        else
            return Math.max(MaxSum(l + 1, r), MaxSum(l + 1, r + 1)) + d[l][r];
    }

}

樣例輸入輸出:

輸入:

6
2
96 30
83 52 60
21 65 44 61
8 79 50 41 21
61 41 50 38 79 10

輸出
375

如果通過記憶化進行優化呢?

思考一下........

....................................................................................................................

public class Hello {
    static int d[][] = new int[100][100]; // 最多100層
    static int max[][]= new int[100][100]; //保存記憶
    static int n;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt(); // 總共有幾層
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= i; j++)
                d[i][j] = sc.nextInt();
        int m = MaxSum(1, 1);
        System.out.println("最大值:"+m);
    }

    static int MaxSum(int l, int r) {
        if (l == n)
            return d[l][r];
        else {
            if(max[l][r]!=0) {
                return max[l][r];
            }else {
                int x=Math.max(MaxSum(l + 1, r), MaxSum(l + 1, r + 1)) + d[l][r];
                max[l][r]=x;
                return x;               
            }
        }
    }
}

源碼、更多資深講師相關課程資料、學習筆記請入羣后向管理員免費獲取,更有專業知識答疑解惑。入羣即送價值499元在線課程一份。

QQ羣號:560819979
敲門磚(驗證信息):高山流水

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章