遞迴與尾遞迴(tail-recursion)
阿新 • • 發佈:2018-12-21
轉載於 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");
}