1. 程式人生 > >基礎演算法:遞迴

基礎演算法:遞迴

遞迴用處很廣,可以將複雜問題簡單化。

很多問題都可以使用遞迴演算法或結合遞迴演算法得到解決。

 

那麼,設計遞迴演算法的關鍵是什麼?  其關鍵之處在於,正確分析出2種類型的節點:出口節點和入口節點

 

一    演算法關鍵: 出口節點 , 入口節點

      遞迴問題可看做是由各個節點構成,而所有節點只能分為出口節點、入口節點兩類。

       1)出口節點:   可直接計算此節點的值,不需藉助其他節點

        那什麼情況下,不必再遞迴了呢?

        確定結果了,自然就不用繼續遞迴了,這也是遞迴的出口, 遞迴的根基。 所以這個根基必須判斷好,這是遞迴是否成功的一個關鍵之處。

        因此,第一個的關鍵是:確定哪些節點是出口節點。

      

        2)入口節點: 無法直接計算此節點的值,但此節點的值可根據後續節點的值計算。

              入口節點與後續節點的數學或邏輯關係, 且如何用遞迴方式表示這個關係,則是遞迴演算法的第二個關鍵。

         

 

二    什麼問題可以用遞迴來處理?

 

      遞迴的過程,就是搜尋的過程,搜尋一個個的節點,找到出口節點。

      若問題滿足下面條件,則可遞迴解決:

      A)  問題可看做為在很多節點中進行搜尋

      B)  這些節點或者為出口節點 或者為入口節點

      C)  入口節點和後續節點間的關係 可用遞迴方式描述

      D)  出口節點在入口節點的後續路徑中

 

下面舉幾個例子:

 

1  遞增

第5個人的年齡比第4個的年齡大2歲,第4個人的年齡比第3個的年齡大2歲,第3個人的年齡比第2個的年齡大2歲,第2個人的年齡比第1個的年齡大2歲,第1個的年齡10歲.

[cpp] view plaincopy

  1. <strong>int age(int n)  
  2. {   
  3.     int c;  
  4.        
  5.     //是否為出口節點,若是,則不需繼續遞迴  
  6.     if(n==1)  
  7.         c=10;  
  8.     else // 入口節點與後續節點的關係  
  9.         c=age(n-1)+2;  
  10.     return c;  
  11. }  
  12. </strong>  


 

2   階乘

 5的階乘是4的階乘*5,4的階乘是3的階乘*4,3的階乘是2的階乘*3,2的階乘是1的階乘*2,1的階乘是1的時候。

 

[cpp] view plaincopy

  1. int fac(int n)  
  2. {   
  3.     int c;  
  4.     if(n==1) c=1;  
  5.     else c=fac(n-1)*n;  // 將前n-1個看做一個處理單元  
  6.     return c;  
  7. }  


3  斐波那契數列

第一個和第二個數分別為1和1,從第三個數開始,每個數等於其前兩個數之和(1,1,2,3,5,8,13…………)

 

[cpp] view plaincopy

  1. int rabbitNum(int n)  
  2. {  
  3.   
  4.     if (n==1||n==2) // 不需遞迴 找到出口  
  5.     {  
  6.         return n;  
  7.     }else{  
  8.                     
  9.         return rabbitNum(n-1)+rabbitNum(n-2);  
  10.   
  11.     }  
  12.   
  13. }  


4 漢諾塔

 

[cpp] view plaincopy

  1. void  Hannoi(int n,TCHAR a,TCHAR b,TCHAR c)  
  2. {  
  3.    
  4.     // 已經確定答案了,不需遞迴的情況  
  5.     if (n==1)  
  6.     {  
  7.         Move(1,a,c);  
  8.   
  9.     }else//需要繼續遞迴的情況, 在這種情況下,關鍵是看:如何將問題劃分為子問題處理單元,然後處理各個單元間的關係  
  10.   
  11.         // 本例:  將n個盤子 分成兩個單元: 前n-1個盤子為一單元 、最後一個盤子為一單元  
  12.           
  13.         Hannoi(n-1,a,c,b);// 將第一個單元 從 a 移動到 b  
  14.         Move(n,a,c);      // 將第二個單元 從 a 移動到 c  
  15.         Hannoi(n-1,b,a,c);// 將第一個單元 從 b 移動到 c  
  16.     }  
  17.   
  18. }  

 

5  將輸入的字元以相反順序打印出來

 

[cpp] view plaincopy

  1. #include <stdio.h>   
  2. #include <string.h>   
  3.   
  4. void strv(TCHAR* p){   
  5.   
  6.     if(!*p)  // 不需繼續遞迴  遞迴出口  
  7.         return;   
  8.     // 當p+1(包括p+1)之後的作為一個處理單元   
  9.     strv(p + 1);   
  10.     // 處理完畢 第一個單元后,輸出本單元*p  
  11.     putchar(*p);   
  12. };   
  13.   
  14. int main(){   
  15.   
  16.     TCHAR* p = _T("ABCDEFGHIJK");   
  17.     strv(p);   
  18.     return 0;   
  19. };