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層以上的部分肯定是滿二叉樹,因此時間複雜度肯定是ω(2
n2)\omega(2^})
ω(22n
)的,再加上其他節點,因此我們可以大概認為時間複雜度為o(2
n)o(2^n)
o(2n
),空間複雜度為樹的深度為o(n
)o(n)
o(n)
實現**
ll fibo
(ll n)
2.尾遞迴
通過改變函式的形式,能夠讓結果在最底層呼叫時直接返回。這樣我們就可以在每一層函式尾呼叫自身來實現尾遞迴。因為編譯器發現函式最後是尾呼叫(直接返回另乙個函式)的時候將不會儲存原函式的棧幀(因為已經執行結束),而是直接帶著上乙個棧幀的結果直接在原地進入下一棧幀。這樣就沒有遞迴呼叫時空間的額外消耗。
對於本問題,使用尾遞迴的話時間複雜度為o(n
)o(n)
o(n)
,空間複雜度為o(1
)o(1)
o(1)
ll fibo2
(ll n,ll x,ll y)
//y儲存當前項,呼叫時x=0,y=1,表示當前項為1
3. 記憶化搜尋
為了避免對已經計算過的值再次計算,我們用乙個陣列儲存已經計算過的值。這樣的時間複雜度將變為o(n
)o(n)
o(n)
,空間複雜度也為o(n
)o(n)
o(n)
ll work3
(ll n, ll* ans)
ll fibo3
(ll n)
4. 遞推
斐波那契有遞推公式:fib
o[n]
=fib
o[n−
1]+f
ibo[
n−2]
fibo[n]=fibo[n-1]+fibo[n-2]
fibo[n
]=fi
bo[n
−1]+
fibo
[n−2
],因此可以用遞推解決。如果記憶所有項的話,空間複雜度為o(n
)o(n)
o(n)
,如果不記憶的話空間複雜度為o(1
)o(1)
o(1)
。時間複雜度為o(n
)o(n)
o(n)
ll fibo4
(ll n)
效能測試#include
#include
#include
using
namespace std;
typedef
long
long ll;
typedef ll (
*fp)
(ll)
;ll fibo1
(ll n)
ll work2
(ll n,ll x=
0,ll y=1)
//y儲存當前項,呼叫時x=0,y=1,表示當前項為1
ll fibo2
(ll n)
ll work3
(ll n, ll* ans)
ll fibo3
(ll n)
ll fibo4
(ll n)
void
test
(ll n,fp fp)
cout<<
"方法"
<
1<<
"平均用時"
<
"s"<
intmain()
; ll n;
cout<<
"請輸入需要查詢斐波那契數列第幾項:"
; cin>>n;
test
(n,fp)
;return0;
}
執行結果
效能分析
可以看到後面幾種方法的複雜度相差不多,但是同樣是遞迴處理,尾遞迴比普通遞迴的複雜度明顯優秀很多,所以如果可以的話應該盡量將遞迴變成尾遞迴的方式進行處理。
斐波那契 尾遞迴
1 計算任意數n的階乘 5 5 4 3 2 1 8 8 7 6 5 4 3 2 1 遞迴函式通過兩個條件出發回的過程 1 當前函式徹底執行完畢的時候,觸發回的過程,回到上一層函式的呼叫處 2 當前函式遇到return 返回值的時,觸發回的過程,回到上一層函式的呼叫處 普通方法 n 5 total 1...
斐波那契數列求解
斐波那契數列是一種常見的數列,其滿足下面兩個條件 f0 f1 1 fn fn 1 fn 2 斐波那契數列求解def fib1 n if n 2 return 1 return fib1 n 1 fib1 n 2 不適用遞迴 python def fib2 n f1 f2 1 for i in ran...
斐波那契 尾遞迴,DP
先看斐波拉契遞迴的樸素版本 int fib1 int n 這段 的意思是 第n個數等於前兩個數之和。但 f 1 1,f 0 0,這兩個特殊值作為遞迴出口。優化 尾遞迴 int fib wei int n int a,int b intmain 這段 明顯可讀性比樸素版本低,但優點在於將時間複雜度從o...