劍指Offer #07 斐波那契數列(四種解法)| 圖文詳解

題目來源:牛客網-劍指Offer專題
題目地址:斐波那契數列

題目描述

大家都知道斐波那契數列,現在要求輸入一個整數n,請你輸出斐波那契數列的第n項(從0開始,第0項爲0)。n<=39

題目解析

方法一:
普通遞歸版求法,這種方法通常和漢諾塔一起被放在課本的遞歸教學部分,應該是面試官不希望看到的算法。
F(n)={0,n=01,n=1,2F(n1)+F(n2),n>2 F(n) = \begin{cases} 0, & \text {n=0} \\ 1, & \text {n=1,2} \\ F(n-1)+F(n-2), & \text{n>2} \end{cases}
利用上面遞推式,自頂向下進行求解,因爲存在大量的重疊子問題,時間複雜度爲 O(2n)O(2^n)

public class Solution {
    public int Fibonacci(int n) {
        if (n == 0) return 0;
        if (n == 1) return 1;
        return Fibonacci(n - 1) + Fibonacci(n - 2);
    }
}

方法二:
我們可以將遞推式的求解從自頂向下改爲自底向上(循環實現)。簡而言之,我們已知前兩項的值,然後我們就可以用前兩項的值求出第3項的值,接着求第4、第5、……,直到求出第n項的值。(廢話)

實現過程如下圖所示,兩個相同顏色的箭頭可以確定一個新的數列項。
1
上述算法的時間複雜度爲 O(n)O(n),在面試中夠用了,如果還是覺得簡單可以繼續往下看。

public class Solution {
    public int Fibonacci(int n) {
        if (n == 0) {
            return 0;
        }
        int a = 1, b = 1;
        for (int i = 1; i <= n - 2; i++) {
            a = a + b;
            b = a - b;
        }
        return a;
    }
}

方法三:
我們知道:
F(n)=F(n1)+F(n2)F(n1)=F(n1)+0F(n2) F(n)=F(n-1)+F(n-2) \\ F(n-1)=F(n-1)+0*F(n-2)
將其轉化成矩陣運算可得

(F(n)F(n1))=(1110)(F(n1)F(n2)) \begin{pmatrix} F(n) \\ F (n-1)\\ \end{pmatrix}= \begin{pmatrix} 1&1 \\ 1&0 \\ \end{pmatrix}* \begin{pmatrix} F(n-1) \\F(n-2)\\ \end{pmatrix}
而右邊的2×12\times1階矩陣又可以進一步分解爲
(F(n1)F(n2))=(1110)(1110)(F(n2)F(n3)) \begin{pmatrix} F(n-1) \\ F (n-2)\\ \end{pmatrix}= \begin{pmatrix} 1&1 \\ 1&0 \\ \end{pmatrix}* \begin{pmatrix} 1&1 \\ 1&0 \\ \end{pmatrix}* \begin{pmatrix} F(n-2) \\F(n-3)\\ \end{pmatrix}
按照這樣一直分解下去直到右邊的2×12\times1階矩陣F(2),F(1),即
(F(n)F(n1))=(1110)n2(F(2)F(1)) \begin{pmatrix} F(n) \\ F (n-1)\\ \end{pmatrix}= \begin{pmatrix} 1&1 \\ 1&0 \\ \end{pmatrix}^{n-2}* \begin{pmatrix} F(2) \\F(1)\\ \end{pmatrix}
這時利用矩陣版的快速冪求解其中的矩陣冪乘,就可以在 O(logn)O(logn) 的時間複雜度下得出Fibonacci數列的第n項的值。

這種方法通常是用在算法比賽中,在面試中容易裝逼失敗不適合使用,這裏也不掛板子了。

方法四:
根據上面的遞推式,利用我們高中學過的“待定係數法”可以推導出斐波那契數列的通項公式。公式如下,(推導過程略)
F(n)=55[(1+52)n(152)n] F(n)=\frac {\sqrt5} {5} [(\frac {1+\sqrt5} {2})^n-(\frac {1-\sqrt5} {2})^n]
公式法時間複雜度爲 O(1)O(1) ? 感覺不然,求公式中的 nn 次方應該要用上快速冪,我個人認爲時間複雜度應該也是 O(logn)O(logn)。(我要滾去看源碼了

public class Solution {
    public int Fibonacci(int n) {
        double a = Math.sqrt(5)/5;
        double b = Math.pow((1+ Math.sqrt(5))/2, n);
        double c = Math.pow((1- Math.sqrt(5))/2, n);
        return (int)(a * (b - c));
    }
}

後記:
如果你在寫出循環版之後,再給面試官描述後面兩種算法,並流暢寫出通項公式的推導過程,相信肯定可以取得面試官的芳心~


如果本文對你有所幫助,要記得點贊哦~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章