遞歸與尾遞歸總結

一、遞歸node

  關於遞歸的概念,咱們都不陌生。簡單的來講遞歸就是一個函數直接或間接地調用自身,是爲直接或間接遞歸。通常來講,遞歸須要有邊界條件、遞歸前進段和遞歸返回段。當邊界條件不知足時,遞歸前進;當邊界條件知足時,遞歸返回。用遞歸須要注意如下兩點:(1) 遞歸就是在過程或函數裏調用自身。(2) 在使用遞歸策略時,必須有一個明確的遞歸結束條件,稱爲遞歸出口。算法

遞歸通常用於解決三類問題:
   (1)數據的定義是按遞歸定義的。(Fibonacci函數,n的階乘)
   (2)問題解法按遞歸實現。(回溯)
   (3)數據的結構形式是按遞歸定義的。(二叉樹的遍歷,圖的搜索)
遞歸的缺點:
  遞歸解題相對經常使用的算法如普通循環等,運行效率較低。所以,應該儘可能避免使用遞歸,除非沒有更好的算法或者某種特定狀況,遞歸更爲適合的時候。 在遞歸調用的過程中系統爲每一層的返回點、局部量等開闢了棧來存儲,所以遞歸次數過多容易形成棧溢出。
  用線性遞歸實現Fibonacci函數,程序以下所示:
複製代碼
1 int FibonacciRecursive(int n) 2 { 3 if( n < 2) 4 return n; 5 return (FibonacciRecursive(n-1)+FibonacciRecursive(n-2)); 6 }
複製代碼

遞歸寫的代碼很是容易懂,徹底是根據函數的條件進行選擇計算機步驟。例如如今要計算n=5時的值,遞歸調用過程以下圖所示:函數

二、尾遞歸測試

  顧名思義,尾遞歸就是從最後開始計算, 每遞歸一次就算出相應的結果, 也就是說, 函數調用出如今調用者函數的尾部, 由於是尾部, 因此根本沒有必要去保存任何局部變量. 直接讓被調用的函數返回時越過調用者, 返回到調用者的調用者去。尾遞歸就是把當前的運算結果(或路徑)放在參數裏傳給下層函數,深層函數所面對的不是愈來愈簡單的問題,而是愈來愈複雜的問題,由於參數裏帶有前面若干步的運算路徑。優化

  尾遞歸是極其重要的,不用尾遞歸,函數的堆棧耗用難以估量,須要保存不少中間函數的堆棧。好比f(n, sum) = f(n-1) + value(n) + sum; 會保存n個函數調用堆棧,而使用尾遞歸f(n, sum) = f(n-1, sum+value(n)); 這樣則只保留後一個函數堆棧便可,以前的可優化刪去。spa

  採用尾遞歸實現Fibonacci函數,程序以下所示:code

複製代碼
1 int FibonacciTailRecursive(int n,int ret1,int ret2) 2 { 3 if(n==0) 4 return ret1; 5 return FibonacciTailRecursive(n-1,ret2,ret1+ret2); 6 }
複製代碼

例如如今要計算n=5時的值,尾遞歸調用過程以下圖所示:blog

從圖能夠看出,爲遞歸不須要向上返回了,可是須要引入而外的兩個空間來保持當前的結果。遞歸

  爲了更好的理解尾遞歸的應用,寫個程序進行練習。採用直接遞歸和尾遞歸的方法求解單鏈表的長度,C語言實現程序以下所示:ci

複製代碼
 1 #include <stdio.h>
 2 #include <stdlib.h>  3  4 typedef struct node  5 {  6 int data;  7 struct node* next;  8 }node,*linklist;  9 10 void InitLinklist(linklist* head) 11 { 12 if(*head != NULL) 13 free(*head); 14 *head = (node*)malloc(sizeof(node)); 15 (*head)->next = NULL; 16 } 17 18 void InsertNode(linklist* head,int d) 19 { 20 node* newNode = (node*)malloc(sizeof(node)); 21 newNode->data = d; 22 newNode->next = (*head)->next; 23 (*head)->next = newNode; 24 } 25 26 //直接遞歸求鏈表的長度 27 int GetLengthRecursive(linklist head) 28 { 29 if(head->next == NULL) 30 return 0; 31 return (GetLengthRecursive(head->next) + 1); 32 } 33 //採用尾遞歸求鏈表的長度,藉助變量acc保存當前鏈表的長度,不斷的累加 34 int GetLengthTailRecursive(linklist head,int *acc) 35 { 36 if(head->next == NULL) 37 return *acc; 38 *acc = *acc+1; 39 return GetLengthTailRecursive(head->next,acc); 40 } 41 42 void PrintLinklist(linklist head) 43 { 44 node* pnode = head->next; 45 while(pnode) 46  { 47 printf("%d->",pnode->data); 48 pnode = pnode->next; 49  } 50 printf("->NULL\n"); 51 } 52 53 int main() 54 { 55 linklist head = NULL; 56 int len = 0; 57 InitLinklist(&head); 58 InsertNode(&head,10); 59 InsertNode(&head,21); 60 InsertNode(&head,14); 61 InsertNode(&head,19); 62 InsertNode(&head,132); 63 InsertNode(&head,192); 64  PrintLinklist(head); 65 printf("The length of linklist is: %d\n",GetLengthRecursive(head)); 66 GetLengthTailRecursive(head,&len); 67 printf("The length of linklist is: %d\n",len); 68 system("pause"); 69 }
複製代碼

程序測試結果以下圖所示:

相關文章
相關標籤/搜索