遞歸(recursive)算法是一種循環調用自身來解決問題的思想,這是一中比較神奇的方法,你只要能口述循環調用過程,然后設定好基礎情況(什么時候開始、什么時候結束),基本根據描述就可以將思路轉換成代碼,遞歸算法有以下條件組成:
1、遞歸開始和結束的基本條件(base case)
2、每次執行需要循環調用自己(也就是找出遞歸方程),每次調用自己時,執行內容都要向基本條件靠攏,直至滿足並跳出基本條件
所以遞歸定義很重要的一點就是要定義好跳出遞歸的基本條件,否則極易引起死循環,進去后就出不來,拿一個最簡單的故事作為例子來說:
從前有個廟,廟里有個老和尚和一個小和尚,老和尚對小和尚說:從前有個廟,廟里有個老和尚和一個小和尚,老和尚對小和尚說......
這個故事也是一個遞歸,但是是一個死的遞歸,因為沒有結束的基本條件,故事開始后老和尚會一直重復將這個故事,結果可想而知,如果是
人的話,則老和尚會被累死故事才能結束,如果是計算機,則程序爆掉了執行才能結束,但是如果有結束條件的話就可以避免這種情況發生,比如老和尚
事先對小和尚說好,這個故事只能講兩遍,那當老和尚講完第二遍后自動結束講述,程序也就至此結束了。遞歸算法案例分析:
1、求解斐波那契數(Fibonacci number)
斐波那契數就是斐波那契數列中的數,斐波那契數的定義是:f(n)=f(n-1)+f(n-2),基本條件是f(n)=n (n<=1)
根據描述,可以定義函數:
def fibonacci(n):
if(n<=1) return n; // 定義結束條件,這時候不能再調用自己了
return fibonacci(n-1)+fibonacci(n-2); // 遞歸方程,調用自己
方法定義完畢,可以求解fibonacci(n),當n=1時,fibonacci(1)=1,遞歸帶入,可以求出fibonacci(3)=2,fibonacci(10)=55
2、移動漢諾塔
漢諾塔是有三根柱子,假定是a,b,c,其中a柱子上有n個圓盤,下層的盤要比上一層的大,需要將這n個盤從a通過b柱子移動到c。
根據上面的需求, 對題解進行遞歸描述:
a、將a柱上的n-1個圓盤借助c柱移動到b柱上面
b、將第n個圓盤移動到c柱上面
c、將b柱上的n-1個圓盤借助a柱移動到c柱上面
所以定義功能如下:
def hanoi(n,a,b,c):
if(n==1) move(a,c);
else{
hanoi(n-1,a,c,b);
move(a,c);
hanoi(n-1,b,a,c);
}
def move( a,c):
System.out.println("move from "+ a+ " to "+c);
3、數據結構的遞歸定義
這類大部分是樹的遍歷及相關計算,樹的先序、中序、后序遍歷可以用以下函數定義:
a、先序遍歷
def preOrder(Tree t):
print(t.value);
preOrder(t.left);
preOrder(t.right);
b、中序遍歷
def preOrder(Tree t):
preOrder(t.left);
print(t.value);
preOrder(t.right);
c、后序遍歷
def preOrder(Tree t):
preOrder(t.right);
preOrder(t.left);
print(t.value);
根據以上模板可以進行一些其他屬性的計算,例如求解樹的深度,樹的深度可以描述為:
- 基本條件:如果樹為null,直接返回0
- 遍歷求解:
- 求解T.left 的深度 leftDep
- 求解T.right的深度 rightDep
- 如果leftDep>rightDep,返回leftDep+1,反之返回rightDep+1
所以求解樹的深度可以定義為:
def TreeDep(Tree t):
if(t==null) return 0;
int leftDep=TreeDep(t.left);
int rightDep=TreeDep(t.right);
return leftDep>rightDep?leftDep+1:rightDep+1;
綜上,滿足遞歸調用的兩個基本特征有:
a、尋找遞歸結束的基本條件
b、定義遞歸解的結構,也就是遞歸方程,最后將口頭描述轉換為代碼結構