LeetCode.509——斐波那契數
問題描述:
斐波那契數,通常用 F(n) 表示,形成的序列稱為斐波那契數列。該數列由 0 和 1 開始,後面的每一項數字都是前面兩項數字的和。也就是:
F(0) = 0, F(1) = 1 F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
給定 N,計算 F(N)。
示例 :
輸入:2 輸出:1 解釋:F(2) = F(1) + F(0) = 1 + 0 = 1.
問題分析:
由於計算任何一個第n(n >= 2)項的數都需要知道其前面兩個數,即需要知道n-1和n-2是多少,然後兩個相加得到結果,但是問題來了,要知道n-1,就要需要知道n-2,要知道n-2就需要知道n-3,會一直這樣的迴圈遞迴下去,一直到第一個數,第二個,第三個.......再反推回來。 那就很明顯了,大家第一時間想到的方法便是遞迴,就下來實現一下:
方法一:遞迴實現
public class Solution { public int fib(int n) { if(n <= 1){ return n; } return fib(n-1) + fib(n-2); } }
問題分析:
先看一下遞迴圖:
由於很多數的計算都要重複很多次,效率並不高,時間複雜度達到了 O(2^n),是斐波那契數計算中 時間複雜度最大,最不可取的方法。
空間複雜度:O(n),堆疊中需要的空間與 N 成正比,堆疊會跟蹤 fib(n) 的呼叫,隨著堆疊的不斷增長 如果沒有足夠的記憶體則會出現StackOverflowError異常。
注:定義為int型時,最大隻能求到n = 46,f(46) = 1836311903, 而 f(47) = -1323752223,因為超出了int 型數值的最大範圍。
演算法改進:
使用遞迴的同時,使用記憶化方式儲存已經計算過的資料,減少不必要的重複計算,可以使時間複雜度降到 O(N),同時空間複雜度也是O(N)。具體的實現是使用一個數組,把每次計算過的值都儲存進去,當再次使用這個數的時候,直接返回,不需要再進行遞迴。
方法二:記憶化自底向上遞迴
public class Solution { public int fib(int n) { if(n <= 1){ return n; } int[] memo = new int[n+1]; memo[1] = 1; for(int i = 2;i <= n; i++){ //自底向上填充陣列,一直到需要的那個數 memo[i] = memo[i-1] + memo[i-2]; } return memo[n]; } }
最後:
限於水平有限,斐波那契數的實現還有很多種方法,不能一一列舉,當其中大部分都有類似的思想。
水文中如有不準確或是錯誤之處,還望指出。謝謝~~~
下一篇:LeetCode.62——不同路徑