斐波那契數列的遞迴演算法與非遞迴演算法
一、斐波那契數列
由於斐波納挈數列是以兔子的繁殖引入的,因此也叫“兔子數列”。它指的是這樣一個數列:0,1,1,2,3,5,8,13......從這組數可以很明顯看出這樣一個規律:從第三個數開始,後邊一個數一定是在其之前兩個數的和。在數學上,斐波納挈數列可以以這樣的公式表示:F(0) = 0F(1) = 1F(n) = F(n-1) + F(n-2),(n>=2)
二、斐波納挈數列的實現
既然該數列已經有這樣一個規律:F(n) = F(n-1) + F(n-2);那麼我們很容易就能想到用遞迴的方法,這樣寫出來的程式碼比較簡潔
當然,我們也可以這樣寫:long long Fib1(long long num) { assert(num >= 0); //遞迴 if ((num == 1) || (num == 0)) { return num; } return Fib1(num-1)+Fib1(num-2); }
long long Fib1(long long num)
{
assert(num >= 0);
//遞迴
return num<2 ? num:(Fib1(num-1)+Fib1(num-2));
}
這樣的遞迴演算法雖然只有簡單的幾行,但是效率卻很低。為什麼呢?我們可以分析其遞迴呼叫的時間複雜度:
時間複雜度 ----- O(2^N)
由於使用遞迴時,其執行步驟是:要得到後一個數之前必須先計算出之前的兩個數,即在每個遞迴呼叫時都會觸發另外兩個遞迴呼叫,例如:要得到F(10)之前得先得到F(9)、F(8),那麼得到F(9)之前得先得到F(8)、F(7)......如此遞迴下去
從上圖我們可以看出,這樣的計算是以 2 的次方在增長的。除此之外,我們也可以看到,F(8)和F(7)的值都被多次計算,如果遞迴的深度越深,那麼F(8)和F(7)的值會被計算更多次,但是這樣計算的結果都是一樣的,除了其中之一外,其餘的都是浪費,可想而知,這樣的開銷是非常恐怖的!
所以,如果在時間複雜度和空間複雜度都有要求的話,我們可以用以下兩種非遞迴演算法來實現:
@@:時間複雜度為O(N),空間複雜度為O(N)
建立一個數組,每次將前兩個數相加後直接賦給後一個數。這樣的話,有N個數就建立一個包含N個數的一維陣列,所以空間複雜度為O(N);由於只需從頭向尾遍歷一邊,時間複雜度為O(N)
long long* Fib2(long long num) { assert(num >= 0); //非遞迴 long long* array = new long long[num+1]; array[0] = 0; array[1] = 1; for (int i=2; i<=num; i++) { array[i] = array[i-1] + array[i-2]; } return array; }
@@:時間複雜度為O(N),空間複雜度為O(1)
藉助兩個變數 first 和 second ,每次將 first 和 second 相加後賦給 third ,再將 second 賦給 first ,third 賦給 second,如此迴圈。
long long Fib3(long long num)
{
assert(num >= 0);
long long first = 0;
long long second = 1;
long long third = 0;
for(int i=2; i<=num; i++)
{
third = first + second;
first = second;
second = third;
}
return third;
}