相信如果乙個人讓我們求乙個斐波那契數列,如果你學過c語言,你一定會說用遞迴法啊,很容易就實現了,但是如果人家讓你求斐波那契的第50個數,而且你對遞迴了解的話,估計幫你不會說遞迴了,如果了解夠深的話,其實你會說遞迴也可以求出來。1、遞迴
首先我們來說說什麼是遞迴,簡單的來說,就是乙個函式需要呼叫自己來完成某種功能,這種呼叫就叫做遞迴。
但我們需要清楚一點,遞迴在使用的時候,並不是一直呼叫自己,我們需要給他乙個停下來的時機。就像打仗一樣,要知道進攻的路線,但如果遇到突發狀況也要能及時撤退。所以我們的遞迴也一樣,你需要給他一條前進路徑也要給他一條返回路徑。所以在使用遞迴策略時,必須有乙個明確的遞迴結束條件,稱為遞迴出口。
但我們都清楚,遞迴有它致命的缺點,在遞迴呼叫的過程當中系統為每一層的返回點、區域性量等開闢了棧來儲存,因此遞迴次數過多容易造成棧溢位,正是因為如此它和迴圈比較效率是很低的,這也就是我前面所說的如果讓你計算斐波那契的第五十個數你直接用遞迴法去求其實是不太好的。
下面我們來看看程式:
1
2
3
4
5
6
int
fib(
int
n)
這樣寫出來的**很簡潔,來分析一下它的執行過程,我們給n=5:
可能這樣你還看不出問題,其實上面的圖相當是乙個樹狀結構:
紅色的部分在之後又會被求到,如果我們給的數值不是5是乙個更大的數,則被重複計算和呼叫的數和次數會變得更多。可見,在這樣乙個過程中,我們把某些值一直在重複計算,再加上重複的開闢棧空間,使得它的效率變得非常低,你們可以試著求一下第40 50個斐波那契額。
2、尾遞迴
尾部遞迴是一種程式設計技巧。如果在遞迴函式中,遞迴呼叫返回的結果總被直接返回,則稱為尾部遞迴。尾部遞迴的函式有助將演算法轉化成函式程式語言,而且從編譯器角度來說,亦容易優化成為普通迴圈。這是因為從電腦的基本面來說,所有的迴圈都是利用重複移跳到**的開頭來實現的。如果有尾部歸遞,就只需要疊套乙個堆疊,因為電腦只需要將函式的引數改變再重新呼叫一次。
1它的執行步驟如下,每次的ret1就是要求當前的返回值,當執行到n減到0的時候,此時的ret1就是我們要求的第n個數:int fib(int n, int ret1, int
ret2)23
8else
912 }
這裡我們在傳參的時候需要傳ret=0,ret2=1;
fib(n - 1, ret2, ret1 +ret2)的使用,
原本樸素的遞迴產生的棧的層次像二叉樹一樣,以指數級增長,但是現在棧的層次卻像是陣列,變成線性增長了,實在是奇妙,總結起來也很簡單,原本棧是先擴充套件開,然後邊收攏邊計算結果,現在卻變成在呼叫自身的同時通過引數來計算。
ret1
就是第n個數,而ret2就是第n與第n+1個數的和,這其實和我們的「迭代」殊途同歸。
我們可以看看迭**出來的斐波那契數列求法。3、迭代法
12
3
4
5
6
7
8
9
10
11
12
13
14
int
fib(
int
n)
return
num3;
}
可以看出迭代法實現的方法其實和我們的尾遞迴法乙個道理,但是迭代法比較通俗易懂,而且和尾遞迴比較起來,因為不用開闢棧空間,所以這三種方法比較起來迭代法是效率最高的。我們在解決實際問題的時候,根據實際要求選擇合適的方法。
斐波那契 尾遞迴
1 計算任意數n的階乘 5 5 4 3 2 1 8 8 7 6 5 4 3 2 1 遞迴函式通過兩個條件出發回的過程 1 當前函式徹底執行完畢的時候,觸發回的過程,回到上一層函式的呼叫處 2 當前函式遇到return 返回值的時,觸發回的過程,回到上一層函式的呼叫處 普通方法 n 5 total 1...
斐波那契 尾遞迴,DP
先看斐波拉契遞迴的樸素版本 int fib1 int n 這段 的意思是 第n個數等於前兩個數之和。但 f 1 1,f 0 0,這兩個特殊值作為遞迴出口。優化 尾遞迴 int fib wei int n int a,int b intmain 這段 明顯可讀性比樸素版本低,但優點在於將時間複雜度從o...
斐波那契數列求解 尾遞迴
1.普通遞迴 這裡觀察f 4 的遞迴樹代替f 10 的遞迴樹 後者比較大,畫不下 使用遞迴求解的時候複雜度為t n t n 1 t n 2 t n t n 1 t n 2 t n t n 1 t n 2 觀察遞迴樹,發現降速最快的是最右邊每次減2,因此n 2 frac 2n 層以上的部分肯定是滿二叉...