1. 程式人生 > >LeetCode.509——斐波那契數

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——不同路徑