深究遞迴和迭代的區別、聯絡、優缺點及例項對比

憶江南的部落格發表於2015-08-10

http://blog.csdn.net/laoyang360/article/details/7855860

1.概念區分

遞迴的基本概念:程式呼叫自身的程式設計技巧稱為遞迴,是函式自己呼叫自己.

一個函式在其定義中直接或間接呼叫自身的一種方法,它通常把一個大型的複雜的問題轉化為一個與原問題相似的規模較小的問題來解決,可以極大的減少程式碼量.遞迴的能力在於用有限的語句來定義物件的無限集合.

使用遞迴要注意的有兩點:

1)遞迴就是在過程或函式裡面呼叫自身;

2)在使用遞迴時,必須有一個明確的遞迴結束條件,稱為遞迴出口.

遞迴分為兩個階段:

1)遞推:把複雜的問題的求解推到比原問題簡單一些的問題的求解;

2)迴歸:當獲得最簡單的情況後,逐步返回,依次得到複雜的解.

利用遞迴可以解決很多問題:如揹包問題,漢諾塔問題,....

斐波那契數列為:0,1,1,2,3,5...

由於遞迴引起一系列的函式呼叫,並且有可能會有一系列的重複計算,遞迴演算法的執行效率相對較低.

迭代:利用變數的原值推算出變數的一個新值.如果遞迴是自己呼叫自己的話,迭代就是A不停的呼叫B.

2.辯證看遞迴和迭代

所謂遞迴,簡而言之就是應用程式自身呼叫自身,以實現層次資料結構的查詢和訪問。遞迴的使用可以使程式碼更簡潔清晰,可讀性更好(對於初學者到不見得),但由於遞迴需要系統堆疊,所以空間消耗要比非遞迴程式碼要大很多,而且,如果遞迴深度太大,可能系統資源會不夠用。

往往有這樣的觀點:能不用遞迴就不用遞迴,遞迴都可以用迭代來代替。

誠然,在理論上,遞迴和迭代在時間複雜度方面是等價的(在不考慮函式呼叫開銷和函式呼叫產生的堆疊開銷),但實際上遞迴確實效率比迭代低,既然這樣,遞迴沒有任何優勢,那麼是不是就,沒有使用遞迴的必要了,那遞迴的存在有何意義呢?

萬物的存在是需要時間的檢驗的,遞迴沒有被歷史所埋沒,即有存在的理由。從理論上說,所有的遞迴函式都可以轉換為迭代函式,反之亦然,然而代價通常都是比較高的。但從演算法結構來說,遞迴宣告的結構並不總能夠轉換為迭代結構,原因在於結構的引申本身屬於遞迴的概念,用迭代的方法在設計初期根本無法實現,這就像動多型的東西並不總是可以用靜多型的方法實現一樣。這也是為什麼在結構設計時,通常採用遞迴的方式而不是採用迭代的方式的原因,一個極典型的例子類似於連結串列,使用遞迴定義及其簡單,但對於記憶體定義(陣列方式)其定義及呼叫處理說明就變得很晦澀,尤其是在遇到環鏈、圖、網格等問題時,使用迭代方式從描述到實現上都變得不現實。因而可以從實際上說,所有的迭代可以轉換為遞迴,但遞迴不一定可以轉換為迭代。

採用遞迴演算法需要的前提條件是,當且僅當一個存在預期的收斂時,才可採用遞迴演算法,否則,就不能使用遞迴演算法。

遞迴其實是方便了程式設計師難為了機器,遞迴可以通過數學公式很方便的轉換為程式。其優點就是易理解,容易程式設計。但遞迴是用機制實現的,每深入一層,都要佔去一塊棧資料區域,對巢狀層數深的一些演算法,遞迴會力不從心,空間上會以記憶體崩潰而告終,而且遞迴也帶來了大量的函式呼叫,這也有許多額外的時間開銷。所以在深度大時,它的時空性就不好了。

而迭代雖然效率高,執行時間只因迴圈次數增加而增加,沒什麼額外開銷,空間上也沒有什麼增加,但缺點就是不容易理解,編寫複雜問題時困難。

因而,“能不用遞迴就不用遞迴,遞迴都可以用迭代來代替”這樣的理解,還是辯證的來看待,不可一棍子打死。*/

1,2部分摘自網路,略有改動,向原作者致敬!

3.個人總結

 

定義

優點

缺點

遞迴

程式呼叫自身的程式設計技巧稱為遞迴

1)大問題化為小問題,可以極大的減少程式碼量;

2)用有限的語句來定義物件的無限集合.

3)程式碼更簡潔清晰,可讀性更好

1)遞迴呼叫函式,浪費空間;

2)遞迴太深容易造成堆疊的溢位;

 

迭代

利用變數的原值推算出變數的一個新值,迭代就是A不停的呼叫B.

1)迭代效率高,執行時間只因迴圈次數增加而增加;

2)沒什麼額外開銷,空間上也沒有什麼增加,

1) 不容易理解;

2) 程式碼不如遞迴簡潔;

3) 編寫複雜問題時困難。

二者關

1) 遞迴中一定有迭代,但是迭代中不一定有遞迴,大部分可以相互轉換。

2) 能用迭代的不用遞迴,遞迴呼叫函式,浪費空間,並且遞迴太深容易造成堆疊的溢位./*相對*/

舉例如下:

  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. //迭代實現斐波那契數列  
  5. long fab_iteration(int index)  
  6. {  
  7.     if(index == 1 || index == 2)  
  8.     {  
  9.         return 1;  
  10.     }  
  11.     else  
  12.     {  
  13.         long f1 = 1L;  
  14.         long f2 = 1L;  
  15.         long f3 = 0;  
  16.         for(int i = 0; i < index-2; i++)  
  17.         {     
  18.             f3 = f1 + f2; //利用變數的原值推算出變數的一個新值  
  19.             f1 = f2;  
  20.             f2 = f3;  
  21.         }  
  22.          return f3;  
  23.     }  
  24. }  
  25.   
  26. //遞迴實現斐波那契數列  
  27.  long fab_recursion(int index)  
  28.  {      
  29.     if(index == 1 || index == 2)  
  30.     {  
  31.         return 1;  
  32.     }  
  33.     else  
  34.     {  
  35.         return fab_recursion(index-1)+fab_recursion(index-2);    //遞迴求值  
  36.     }  
  37. }  
  38.   
  39. int main(int argc, char* argv[])  
  40. {  
  41.     cout << fab_recursion(10) << endl;  
  42.     cout << fab_iteration(10) << endl;  
  43.     return 0;  
  44. }  

相關文章