遞歸(c++)(轉)


1.什么是遞歸函數(recursive function)

  遞歸函數即自調用函數,在函數體內部直接或間接地自己調用自己,即函數的嵌套調用是函數本身。
  例如,下面的程序為求n!:
    

long fact(int n) 
    { 
     if(n==1) 
     return 1; 
     return fact(n-1)*n; //出現函數自調用 
    } 

  



2.函數調用機制的說明

  任何函數之間不能嵌套定義, 調用函數與被調用函數之間相互獨立(彼此可以調用)。 發生函數調用時,被調函數中保護了調用函數的運行環境和返回地址,使得調用函數的狀態可以在被調函數運行返回后完全恢復,而且該狀態與被調函數無關。
  被調函數運行的代碼雖是同一個函數的代碼體,但由於調用點,調用時狀態, 返回點的不同,可以看作是函數的一個副本,與調用函數的代碼無關,所以函數的代碼是獨立的。被調函數運行的棧空間獨立於調用函數的棧空間,所以與調用函數之間的數據也是無關的。函數之間靠參數傳遞和返回值來聯系,函數看作為黑盒。
  這種機制決定了C/C++允許函數遞歸調用。

3.遞歸調用的形式

  遞歸調用有直接遞歸調用和間接遞歸調用兩種形式。
  直接遞歸即在函數中出現調用函數本身。
  例如,下面的代碼求斐波那契數列第n項。 斐波那契數列的第一和第二項是1,后面每一項是前二項之和,即1,1,2,3,5,8,13,...。 代碼中采用直接遞歸調用:
   

 long fib(int x) 
    { 
     if(x>2) 
      return(fib(x-1)+fib(x-2)); //直接遞歸 
     else 
      return 1; 
    } 

  


  間接遞歸調用是指函數中調用了其他函數,而該其他函數卻又調用了本函數。例如,下面的代碼定義兩個函數,它們構成了間接遞歸調用:
    

int fnl(int a) 
    { 
     int b; 
     b=fn2(a+1); //間接遞歸 
           //... 
    } 
    int fn2(int s) 
    { 
     int c; 
     c=fnl(s-1); //間接遞歸 
           //... 
    } 

  


  上例中,fn1()函數調用了fn2()函數,而fn2()函數又調用了fn1()函數。

4.遞歸的條件

  (1)須有完成函數任務的語句。
  例如,下面的代碼定義了一個遞歸函數:
    

#include 

    void count(int val) //遞歸函數可以沒有返回值 
    { if(val>1) 
       count(val-1); 、 
     cout<<"ok:" <<<="" 此語句完成函數任務="" /> 

  


  該函數的任務是在輸出設備上顯示"ok:整數值”。
  (2)—個確定是否能避免遞歸調用的測試
  例如,上例的代碼中,語句"if(val>1)"便是—個測試, 如果不滿足條件,就不進行遞歸調用。
  (3)一個遞歸調用語句。
該遞歸調用語句的參數應該逐漸逼近不滿足條件,以至最后斷絕遞歸。
  例如,上面的代碼中,語句“if(val>1)” 便是一個遞歸調用,參數在漸漸變小,這種發展趨勢能使測試"if(val>1)”最終不滿足。
  (4)先測試,后遞歸調用。
在遞歸函數定義中,必須先測試,后遞歸調用。也就是說,遞歸調用是有條件的,滿足了條件后,才可以遞歸。
  例如,下面的代碼無條件調用函數自己,造成無限制遞歸,終將使棧空間溢出:
   

 #include 
    void count(int val) 
    { 
     count(val-1); //無限制遞歸 
     if(val>1) //該語句無法到達 
      cout <<"ok: " <<    } 

  


5.消去遞歸

  大多數遞歸函數都能用非遞歸函數來代替。例如,下面的代碼求兩個整數a,b的最大公約數,用遞歸和非遞歸函數分別定義之:
    

long gcdt(int a,int b) //遞歸版 
    { 
     if(a%b==0) 
      return b; 
     return gcdl(b,a%b); 
    } 
    long gcd2(int a,int b) //非遞歸版 
    { 
      int temp; 
      while(b!=0) 
      { 
       temp=a%b; 
       a=b; 
       b=temp; 
      } 
      return a; 
    } 

  


  思考:將求n!的遞歸函數非遞歸化。

6.遞歸的評價

  遞歸的目的是簡化程序設計,使程序易讀。
  但遞歸增加了系統開銷。 時間上, 執行調用與返回的額外工作要占用CPU時間。空間上,隨着每遞歸一次,棧內存就多占用一截。
  相應的非遞歸函數雖然效率高,但卻比較難編程,而且相對來說可讀性差。
  現代程序設計的目標主要是可讀性好。隨着計算機硬件性能的不斷提高,程序在更多的場合優先考慮可讀而不是高效,所以,鼓勵用遞歸函數實現程序思想。

 

 

(轉自 http://www.cnblogs.com/seaven/archive/2010/12/17/1908953.html)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM