一、什么是遞歸?
- 遞歸是一種非常高效、簡潔的編碼技巧,一種應用非常廣泛的算法,比如DFS深度優先搜索、前中后序二叉樹遍歷等都是使用遞歸。
- 方法或函數調用自身的方式稱為遞歸調用,調用稱為遞,返回稱為歸。
- 基本上,所有的遞歸問題都可以用遞推公式來表示,比如
f(n) = f(n-1) + 1;
f(n) = f(n-1) + f(n-2);
f(n)=n*f(n-1);
二、為什么使用遞歸?遞歸的優缺點?
- 優點:代碼的表達力很強,寫起來簡潔。
- 缺點:空間復雜度高、有堆棧溢出風險、存在重復計算、過多的函數調用會耗時較多等問題。(函數調用會使用棧來保存臨時變量。每調用一個函數,都會將臨時變時變量封裝為棧幀壓入內存棧,等函數執行完成返回時,才出棧。系統棧或者虛擬機棧空間一般都不大。如果遞歸求解的數據規模很大。如果遞歸求解的數據規模很大,調用層次很深,一直壓入棧,就會有堆棧溢出的風險。)
三、什么樣的問題可以用遞歸解決呢?
一個問題只要同時滿足以下3個條件,就可以用遞歸來解決:
- 問題的解可以分解為幾個子問題的解。何為子問題?就是數據規模更小的問題。
- 問題與子問題,除了數據規模不同,求解思路完全一樣
- 存在遞歸終止條件
四、如何實現遞歸?
- 遞歸代碼編寫
寫遞歸代碼的關鍵就是找到如何將大問題分解為小問題的規律,并且基于此寫出遞推公式,然后再推敲終止條件,最后將遞推公式和終止條件翻譯成代碼。 - 遞歸代碼理解
對于遞歸代碼,若試圖想清楚整個遞和歸的過程,實際上是進入了一個思維誤區。
那該如何理解遞歸代碼呢?如果一個問題A可以分解為若干個子問題B、C、D,你可以假設子問題B、C、D已經解決。而且,你只需要思考問題A與子問題B、C、D兩層之間的關系即可,不需要一層層往下思考子問題與子子問題,子子問題與子子子問題之間的關系。屏蔽掉遞歸細節,這樣子理解起來就簡單多了。
因此,理解遞歸代碼,就把它抽象成一個遞推公式,不用想一層層的調用關系,不要試圖用人腦去分解遞歸的每個步驟。
五、遞歸常見問題及解決方案
- 警惕堆棧溢出:可以聲明一個全局變量來控制遞歸的深度,從而避免堆棧溢出。
- 警惕重復計算:通過某種數據結構來保存已經求解過的值,從而避免重復計算。