數據結構與算法之美學習筆記(10章) 遞歸

第10章 遞歸

遞歸很重要,這篇全部是遞歸的知識和練習

一個很好的例子幫助理解

週末你帶着女朋友去電影院看電影,女朋友問你,咱們現在坐在第幾排啊?電影院裏面太黑了,看不清,沒法數,現在你怎麼辦?

別忘了你是程序員,這個可難不倒你,遞歸就開始排上用場了。於是你就問前面一排的人他是第幾排,你想只要在他的數字上加一,就知道自己在哪一排了。但是,前面的人也看不清啊,所以他也問他前面的人。就這樣一排一排往前問,直到問到第一排的人,說我在第一排,然後再這樣一排一排再把數字傳回來。直到你前面的人告訴你他在哪一排,於是你就知道答案了。

這就是一個非常標準的遞歸求解問題的分解過程,去的過程叫“遞”,回來的過程叫“歸”。基本上,所有的遞歸問題都可以用遞推公式來表示。剛剛這個生活中的例子,我們用遞推公式將它表示出來就是這樣的:

f(n)=f(n-1)+1 其中,f(1)=1

f(n) 表示你想知道自己在哪一排,f(n-1) 表示前面一排所在的排數,f(1)=1 表示第一排的人知道自己在第一排。有了這個遞推公式,我們就可以很輕鬆地將它改爲遞歸代碼,如下:

int f(int n) {
  if (n == 1) return 1;
  return f(n-1) + 1;
}

例子2

假如這裏有 n 個臺階,每次你可以跨 1 個臺階或者 2 個臺階,請問走這 n 個臺階有多少種走法?如果有 7 個臺階,你可以 2,2,2,1 這樣子上去,也可以 1,2,1,1,2 這樣子上去,總之走法有很多,那如何用編程求得總共有多少種走法呢?

我們仔細想下,實際上,可以根據第一步的走法把所有走法分爲兩類,第一類是第一步走了 1 個臺階,另一類是第一步走了 2 個臺階。所以 n 個臺階的走法就等於先走 1 階後,n-1 個臺階的走法 加上先走 2 階後,n-2 個臺階的走法。用公式表示就是:

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

有了遞推公式,遞歸代碼基本上就完成了一半。我們再來看下終止條件。當有一個臺階時,我們不需要再繼續遞歸,就只有一種走法。所以 f(1)=1。這個遞歸終止條件足夠嗎?我們可以用 n=2,n=3 這樣比較小的數試驗一下。

n=2 時,f(2)=f(1)+f(0)。如果遞歸終止條件只有一個 f(1)=1,那 f(2) 就無法求解了。所以除了 f(1)=1 這一個遞歸終止條件外,還要有 f(0)=1,表示走 0 個臺階有一種走法,不過這樣子看起來就不符合正常的邏輯思維了。所以,我們可以把 f(2)=2 作爲一種終止條件,表示走 2 個臺階,有兩種走法,一步走完或者分兩步來走。

所以,遞歸終止條件就是 f(1)=1,f(2)=2。這個時候,你可以再拿 n=3,n=4 來驗證一下,這個終止條件是否足夠並且正確。

我們把遞歸終止條件和剛剛得到的遞推公式放到一起就是這樣的:

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

最終代碼

int f(int n) {
  if (n == 1) return 1;
  if (n == 2) return 2;
  return f(n-1) + f(n-2);
}

遞歸需要滿足的三個條件

1. 一個問題的解可以分解爲幾個子問題的解

何爲子問題?子問題就是數據規模更小的問題。比如,前面講的電影院的例子,你要知道,“自己在哪一排”的問題,可以分解爲“前一排的人在哪一排”這樣一個子問題。

2. 這個問題與分解之後的子問題,除了數據規模不同,求解思路完全一樣

比如電影院那個例子,你求解“自己在哪一排”的思路,和前面一排人求解“自己在哪一排”的思路,是一模一樣的。

3. 存在遞歸終止條件

把問題分解爲子問題,把子問題再分解爲子子問題,一層一層分解下去,不能存在無限循環,這就需要有終止條件。

還是電影院的例子,第一排的人不需要再繼續詢問任何人,就知道自己在哪一排,也就是 f(1)=1,這就是遞歸的終止條件。

寫遞歸代碼的關鍵就是找到如何將大問題分解爲小問題的規律,並且基於此寫出遞推公式,然後再推敲終止條件,最後將遞推公式和終止條件翻譯成代碼

編寫遞歸代碼的關鍵是,只要遇到遞歸,我們就把它抽象成一個遞推公式,不用想一層層的調用關係,不要試圖用人腦去分解遞歸的每個步驟

不過遞歸代碼也比較難寫、難理解。編寫遞歸代碼的關鍵就是不要把自己繞進去,正確姿勢是寫出遞推公式,找出終止條件,然後再翻譯成遞歸代碼。

遞歸代碼雖然簡潔高效,但是,遞歸代碼也有很多弊端。比如,堆棧溢出、重複計算、函數調用耗時多、空間複雜度高等,所以,在編寫遞歸代碼的時候,一定要控制好這些副作用。

 

遞歸的練習

1. 現有數字 1,12,123,1234,12345........請問第n個數時多少.(n<9)

遞歸公式 規律

f(n)=f(n-1)*10+n

結束條件

f(1)=1

代碼

public class Test01 {
    public static int f(int n){
        if(n==1){
            return 1;
        }
        return f(n-1)*10+n;
    }

    public static void main(String[] args) {
        System.out.println(f(9));
    }
}

 

 

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