遞迴與尾遞迴

2021-07-01 20:03:05 字數 1815 閱讀 7922

1、遞迴

簡單的來說遞迴就是乙個函式直接或間接地呼叫自身,是為直接或間接遞迴。一般來說,遞迴需要有邊界條件、遞迴前進段和遞迴返回段。當邊界條件不滿足時,遞迴前進;當邊界條件滿足時,遞迴返回。用遞迴需要注意以下兩點:

(1) 遞迴就是在過程或函式裡呼叫自身。(2) 在使用遞迴策略時,必須有乙個明確的遞迴結束條件,稱為遞迴出口。

遞迴一般用於解決三類問題:

(1)資料的定義是按遞迴定義的。(fibonacci函式,n的階乘)

(2)問題解法按遞迴實現。(回溯)

(3)資料的結構形式是按遞迴定義的。(二叉樹的遍歷,圖的搜尋)

遞迴的缺點:

遞迴解題相對常用的演算法如普通迴圈等,執行效率較低。因此,應該盡量避免使用遞迴,除非沒有更好的演算法或者某種特定情況,遞迴更為適合的時候。

在遞迴呼叫的過程當中系統為每一層的返回點、區域性量等開闢了棧來儲存,因此遞迴次數過多容易造成棧溢位。

用線性遞迴實現fibonacci函式,程式如下所示:

1

int fibonaccirecursive(intn)2

遞迴寫的**非常容易懂,完全是根據函式的條件進行選擇計算機步驟。例如現在要計算n=5時的值,遞迴呼叫過程如下圖所示:

2、尾遞迴

如果乙個函式中所有遞迴形式的呼叫都出現在函式的末尾,我們稱這個遞迴函式是尾遞迴的。當遞迴呼叫是整個函式體中最後執行的語句且它的返回值不屬於表示式的一部分時,這個遞迴呼叫就是尾遞迴。

尾遞迴就是從最後開始計算, 每遞迴一次就算出相應的結果, 也就是說, 函式呼叫

出現在呼叫者函式的尾部, 因為是尾部, 所以根本沒有必要去儲存任何區域性變數

. 直接讓被呼叫的函式返回時越過呼叫者, 返回到呼叫者的呼叫者去。尾遞迴就是把當前的運算結果(或路徑)放在引數裡傳給下層函式

,深層函式所面對的不是越來越簡單的問題,而是越來越複雜的問題,因為引數裡帶有前面若干步的運算路徑。

尾遞迴是極其重要的,不用尾遞迴,函式的堆疊耗用難以估量,需要儲存很多中間函式的堆疊。比如f(n, sum) = f(n-1) + value(n) + sum; 會儲存n個函式呼叫堆疊,而使用尾遞迴f(n, sum) = f(n-1, sum+value(n)); 這樣則只保留後乙個函式堆疊即可,之前的可優化刪去。 

尾遞迴函式的特點是在回歸過程中不用做任何操作,

當編譯器檢測到乙個函式呼叫是尾遞迴的時候,它就覆蓋當前的活動記錄而不是在棧中去建立乙個新的。編譯器可以做到這點,因為遞迴呼叫是當前活躍期內最後一條待執行的語句,於是當這個呼叫返回時棧幀中並沒有其他事情可做,因此也就沒有儲存棧幀的必要了。通過覆蓋當前的棧幀而不是在其之上重新新增乙個,這樣所使用的棧空間就大大縮減了,這使得實際的執行效率會變得更高。

(注1:事實上,在c#中,即使您實現了尾遞迴,編譯器(包括c#編譯器及jit)也不會進行優化,也就是說還是無法避免stackoverflowexception。)

尾遞迴的過程和迴圈基本上是等價的,我們可以將尾遞迴的過程很方便到用迴圈來代替,把尾遞迴優化為「迴圈」方式。

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

1

int fibonaccitailrecursive(int n,int ret1,int

ret2)

2

例如現在要計算n=5時的值,尾遞迴呼叫過程如下圖所示:

從圖可以看出,尾遞迴不需要向上返回了,但是需要引入而外的兩個空間來保持當前的結果。

遞迴與尾遞迴

1 遞迴 關於遞迴的概念,我們都不陌生。簡單的來說遞迴就是乙個函式直接或間接地呼叫自身,是為直接或間接遞迴。一般來說,遞迴需要有邊界條件 遞迴前進段和遞迴返回段。當邊界條件不滿足時,遞迴前進 當邊界條件滿足時,遞迴返回。用遞迴需要注意以下兩點 1 遞迴就是在過程或函式裡呼叫自身。2 在使用遞迴策略時...

遞迴與尾遞迴

前言 今天上網看帖子的時候,看到關於尾遞迴的應用 大腦中感覺這個詞好像在 見過,但是又想不起來具體是怎麼回事。如是乎,在網上搜了一下,頓時豁然開朗,知道尾遞迴是怎麼回事了。下面就遞迴與尾遞迴進行總結,以方便日後在工作中使用。1 遞迴 關於遞迴的概念,我們都不陌生。簡單的來說遞迴就是乙個函式直接或間接...

遞迴與尾遞迴

一 遞迴 1.定義 在電腦科學領域中,遞迴式通過遞迴函式來實現的。程式呼叫自身的程式設計技巧稱為遞迴 recursion 乙個過程或函式在其定義或說明中有直接或間接呼叫自身的一種方法,它通常把乙個大型複雜的問題層層轉化為乙個與原問題相似的規模較小的問題來求解,遞迴策略只需少量的程式就可描述出解題過程...