1. 程式人生 > >遞迴與尾遞迴(tail-recursion)

遞迴與尾遞迴(tail-recursion)

轉載於 https://blog.csdn.net/ywcpig/article/details/52749960

遞迴:

一般來說,遞迴需要有邊界條件遞迴前進段遞迴返回段。當邊界條件不滿足時,遞迴前進;當邊界條件滿足時,遞迴返回。用遞迴需要注意以下兩點:(1) 遞迴就是在過程或函式裡呼叫自身。(2) 在使用遞迴策略時,必須有一個明確的遞迴結束條件,稱為遞迴出口。
遞迴一般用於解決三類問題:
  (1)資料的定義是按遞迴定義的。(Fibonacci函式,n的階乘)
  (2)問題解法按遞迴實現。(回溯)
  (3)資料的結構形式是按遞迴定義的。(二叉樹的遍歷,圖的搜尋)
遞迴的缺點:
  遞迴解題相對常用的演算法如普通迴圈等,執行效率較低

。因此,應該儘量避免使用遞迴,除非沒有更好的演算法或者某種特定情況,遞迴更為適合的時候。在遞迴呼叫的過程當中系統為每一層的返回點、區域性量等開闢了棧來儲存,因此遞迴次數過多容易造成棧溢位

在這裡插入圖片描述

尾遞迴:

顧名思義,尾遞迴就是從最後開始計算, 每遞迴一次就算出相應的結果, 也就是說, 函式調用出現在呼叫者函式的尾部, 因為是尾部, 所以根本沒有必要去儲存任何區域性變數. 直接讓被呼叫的函式返回時越過呼叫者, 返回到呼叫者的呼叫者去。尾遞迴就是把當前的運算結果(或路徑)放在引數裡傳給下層函式,深層函式所面對的不是越來越簡單的問題,而是越來越複雜的問題,因為引數裡帶有前面若干步的運算路徑。
  尾遞迴是極其重要的,不用尾遞迴,函式的堆疊耗用難以估量,需要儲存很多中間函式的堆疊。比如f(n, sum) = f(n-1) + value(n) + sum; 會儲存n個函式呼叫堆疊,而使用尾遞迴f(n, sum) = f(n-1, sum+value(n)); 這樣則只保留後一個函式堆疊即可,之前的可優化刪去。

採用尾遞迴實現Fibonacci函式,程式如下所示:

int FibonacciTailRecursive(int n,int ret1,int ret2){
   if(n==0)
       return ret1; 
   return FibonacciTailRecursive(n-1,ret2,ret1+ret2);
}

在這裡插入圖片描述
可以看出,尾遞迴不需要向上返回了,但是需要引入額外的兩個空間來保持當前的結果。
或者如:

#include <stdio.h>
#include <stdlib.h>
 
typedef struct node
{
  int
data; struct node* next; }node,*linklist; void InitLinklist(linklist* head) { if(*head != NULL) free(*head); *head = (node*)malloc(sizeof(node)); (*head)->next = NULL; } void InsertNode(linklist* head,int d) { node* newNode = (node*)malloc(sizeof(node)); newNode->data = d; newNode->next = (*head)->next; (*head)->next = newNode; } //直接遞迴求連結串列的長度 int GetLengthRecursive(linklist head) { if(head->next == NULL) return 0; return (GetLengthRecursive(head->next) + 1); } //採用尾遞迴求連結串列的長度,藉助變數acc儲存當前連結串列的長度,不斷的累加 int GetLengthTailRecursive(linklist head,int *acc) { if(head->next == NULL) return *acc; *acc = *acc+1; return GetLengthTailRecursive(head->next,acc); } void PrintLinklist(linklist head) { node* pnode = head->next; while(pnode) { printf("%d->",pnode->data); pnode = pnode->next; } printf("->NULL\n"); } int main() { linklist head = NULL; int len = 0; InitLinklist(&head); InsertNode(&head,10); InsertNode(&head,21); InsertNode(&head,14); InsertNode(&head,19); InsertNode(&head,132); InsertNode(&head,192); PrintLinklist(head); printf("The length of linklist is: %d\n",GetLengthRecursive(head)); GetLengthTailRecursive(head,&len); printf("The length of linklist is: %d\n",len); system("pause"); }