遞迴
不知道有新手聽沒聽過別人拿剝糖塊來形容遞迴,諸如一層層地剝好比一層層地進入遞迴。這種比喻可是誤導了我,只想著剝了,其實剝完皮兒,取出糖塊,再把皮兒一層層地穿上才算個完整的遞迴。
遞迴就是自己調用自己的函數或方法了,一般情況,像我這樣的新手剛接觸遞迴的時候,迷就迷在了不明白遞迴的原理上,在 (c#)資料結構與演算法分析 --棧與隊列 中說過,編譯器一般用棧來實現遞迴,具體就看那篇文章吧。
這裡先舉一個用到遞迴的例子。
求第n項的三角數字,三角數字就是數列中,第n項的值是第n-1項加上n得來的。這裡可不是那個斐波那契數列。
1,3,6,10,15,21......... 這些個數就是三角數字。
這個遞迴方法就是計算第n項的三角數字: 1 //使用遞迴求第n項的三角數
2 private int triangle(int n)
3 {
4 if (n == 1) //這是遞迴的基準情形,一個遞迴沒有基準的話,就沒法跳出遞迴。
5 {
6 return n;
7 }
8 else
9 {
10 return (n + triangle(n - 1)); //調用本方法
11 }
12 }
很簡單的一個演算法,可用 第n個三角數字=(n*n+n)/2 這個公式來驗證其正確性。
仔細看看這個圖就會完全明白遞迴到底怎麼遞迴了。
n=5時,開始調用
↓
第1層
↓
返回15
(這個表格在firefox下顯示有點問題)
這時,應該把遞迴理順了吧,自己可以試試用遞迴求階乘,然後試著解決漢諾塔問題,google搜一下會有很多漢諾塔問題的源碼。
很明顯,上面那個遞迴是尾遞迴,在某些情況下,比如函數體比較龐大,有很多局部變數,則很容易引起棧溢出。有時候應該消除遞迴。
這就用到棧了,下面這個源碼,功能和上面一樣,只不過用棧來消除遞迴了。 1 //消除遞迴,使用棧代替遞迴
2 private int triangleStack(int n)
3 {
4 Stack<int> traingle = new Stack<int>(); //這個棧來類比遞迴中的環境變數
5
6 while (n >= 1) //相當於遞迴中的基準了
7 {
8 traingle.Push(n); //將每次遞減的值壓入棧,相當於逐層調用遞迴
9 n = n - 1;
10 }
11
12 while (traingle.Count > 0) //這個相當於逐層跳出遞迴
13 {
14 n = n + traingle.Pop(); //計算
15 }
16
17 return n;
18 }
不是很難吧,主要把遞迴的原理理清,思路自然就明了了。
要注意的是,使用遞迴千萬可別忘了基準情形,不然就永遠遞迴不出來了。遞迴的效率有時候比較低,這樣就可以用棧或迴圈來代替遞迴了。