數據結構與算法學習筆記之高效、簡潔的編碼技巧「遞歸」

前言

盜夢空間想象大多數人都看過:電影講述的是主人公諾蘭進入希裏安·墨菲夢境植入想法的行動。爲了向希裏安·墨菲夢植入理念,影片進入四層夢境,即所謂:「夢中的夢中 夢中人的夢中」。html

有一對兔子,每隔三個月會產下一對小兔子,小免子每隔三個月,也會產生新的一對免子,問36個月後,共有多少對兔子。算法

諸如此類:其實就是「遞歸」,今天就一塊兒進入「遞歸」的世界看看數組

 

正文

1、遞歸的定義


1.遞歸是一種應用普遍的算法,既能運用到軟件開發中成爲高效、簡潔的編碼技巧也能應用到生活中解決實踐遞歸問題,好比DFS深度優先搜索、前中後序二叉樹遍歷等,又好比計算不斷繁衍的後臺個數等等;網絡

2.程序調用自身的方式稱爲遞歸調用,去調用的過程稱爲遞,回來的過程稱爲歸。數據結構

3.基本上,全部的遞歸問題均可以用遞推公式來表示,好比函數

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

2、爲何使用遞歸?

1.遞歸在解決某些問題的時候使得咱們思考的方式得以簡化,代碼也更加精煉,容易閱讀
2.遞歸在處理問題時要反覆調用函數,這增大了它的空間和時間開銷,空間複雜度高、有堆棧溢出風險、存在重複計算、過多的函數調用會耗時較多等問題。性能

 

3、什麼樣的問題能夠用遞歸解決呢?

一個問題只要同時知足如下3個條件,就能夠用遞歸來解決:
1.問題的解能夠分解爲幾個子問題的解。何爲子問題?就是數據規模更小的問題。
2.問題與子問題,除了數據規模不一樣,求解思路徹底同樣
3.存在遞歸終止條件,不能無限循環。

學習

4、如何實現遞歸

1.遞歸代碼編寫


寫遞歸代碼的關鍵就是將大問題分解爲小問題,寫出遞推公式,找出終止條件,最後將遞推公式和終止條件翻譯成代碼。編碼

舉一個栗子:

假如這裏有n個臺階,每一次你能夠跨過一或二個臺階,請問有幾種走法?

根據第一步的走法把走法分爲兩類,第一步走一個臺階或者走兩個臺階,因此n個臺階的走法就等於先走一階的走法加上先走兩個臺階的走法,遞歸公式爲:

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

當只有一個臺階時,咱們就不須要遞歸了,因此終止條件爲:

f(1)=1

可是隻有它還不足夠,n=2時,f(2)=f(1)+f(0)還有f(0)=1,也就是第0階也要有一種走法,不和邏輯,因此終止條件還有一個:

f(2)=2

編寫爲代碼爲:

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

 

2.遞歸代碼理解

對於遞歸代碼,若試圖想清楚整個遞和歸的過程,其實是進入了一個思惟誤區。
那該如何理解遞歸代碼呢?若是一個問題A能夠分解爲若干個子問題B、C、D,你能夠假設子問題B、C、D已經解決。並且,你只須要思考問題A與子問題B、C、D兩層之間的關係便可,不須要一層層往下思考子問題與子子問題,子子問題與子子子問題之間的關係。屏蔽掉遞歸細節,這樣子理解起來就簡單多了。
所以,理解遞歸代碼,就把它抽象成一個遞推公式,不用想一層層的調用關係,不要試圖用人腦去分解遞歸的每一個步驟。

5、遞歸常見問題及解決方案

1.警戒堆棧溢出:能夠聲明一個全局變量來控制遞歸的深度,從而避免堆棧溢出。

代碼實現:

// 全局變量,表示遞歸的深度。
int depth = 0; int f(int n) { ++depth; if (depth > 1000) throw exception; if (n == 1) return 1; return f(n-1) + 1; }

 


2.警戒重複計算:經過某種數據結構來保存已經求解過的值,從而避免重複計算。

如用散列表來保存已存在的值,改寫上面的代碼以下:

public int f(int n) { if (n == 1) return 1; if (n == 2) return 2; // hasSolvedList 能夠理解成一個 Map,key 是 n,value 是 f(n)
  if (hasSolvedList.containsKey(n)) { return hasSovledList.get(n); } int ret = f(n-1) + f(n-2); hasSovledList.put(n, ret); return ret; }

(代碼來源於網絡)

6、如何將遞歸改寫爲非遞歸代碼?

籠統的講,全部的遞歸代碼均可以改寫爲迭代循環的非遞歸寫法。如何作?抽象出遞推公式、初始值和邊界條件,而後用迭代循環實現。

將上面的代碼實現以下:

 

int f(int n) { if (n == 1) return 1; if (n == 2) return 2; int ret = 0; int pre = 2; int prepre = 1; for (int i = 3; i <= n; ++i) { ret = pre + prepre; prepre = pre; pre = ret; } return ret; }

 

相關文章

 

數據結構與算法學習筆記之寫鏈表代碼的正確姿式(下)

 

數據結構與算法學習筆記之 提升讀取性能的鏈表(上)

 

數據結構與算法學習筆記之 從0編號的數組

 

數據結構與算法學習筆記以後進先出的「桶」

 

數據結構與算法學習筆記之先進先出的隊列

 

以上內容爲我的的學習筆記,僅做爲學習交流之用。

 

歡迎你們關注公衆號,不定時乾貨,只作有價值的輸出

做者:Dawnzhang 
出處:http://www.javashuo.com/article/p-ebesfkbh-dm.html

版權:本文版權歸做者轉載:歡迎轉載,但未經做者贊成,必須保留此段聲明;必須在文章中給出原文鏈接;不然必究法律責任

相關文章
相關標籤/搜索