遞歸函數理解

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,
1235813,...。 代碼中採用直接遞歸調用:   
      
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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章