力扣之斐波那契數列

水冗水孚發表於2022-06-30

題目描述

寫一個函式,輸入 n ,求斐波那契(Fibonacci)數列的第 n 項(即 F(N))。斐波那契數列的定義如下:

F(0) = 0,   F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.

斐波那契數列由 0 和 1 開始,之後的斐波那契數就是由之前的兩數相加而得出。
如:fib(2) == 1fib(5) == 5

力扣原題目地址:https://leetcode.cn/problems/...

解決方案

方案一 直接遞迴

遞迴自己呼叫自己,不斷執行,直到遇到某一條件停止遞迴

尋找規律

第0項 == 0
第1項 == 1
第2項 == 第1項 + 第0項
第3項 == 第2項 + 第1項
第4項 == 第3項 + 第2項
......
第i項 == 第i-1項 + 第i-2項 // 從第2項開始,即 i >= 2

使用遞迴表示規律

function fib(i) {
    if (i == 0) {
        return 0
    }
    if (i == 1) {
        return 1
    }
    if (i >= 2) {
        /**
        * 這句話可以理解為:fib(i)函式執行的結果值等於return fib(i - 1) + fib(i - 2)
        * 即:fib(i) == fib(i - 1) + fib(i - 2)
        * 符合上方規律:第i項 == 第i-1項 + 第i-2項 // 從第2項開始,即 i >= 2
        */ 
        return fib(i - 1) + fib(i - 2) 
    }
}
console.time()
console.log('斐波那契第10項', fib(10)); // 55
console.timeEnd() // default: 0.279052734375 ms

我們使用timeEnd()列印出fib(10)第十項大致遞迴執行時間,發現是279毫秒。這裡是有點慢了,原因舉例:

比如我們要執行fib(4)找到第四項的值,在尋找規律中我們不難發現,第二項和第一項都重複計算了,浪費效能,所以這種方式可以實現,但是由於重複計算導致效能並不是太好(所以遞迴寫法在力扣中,是不通過的)

接下來,我們看看能不能減少重複計算,思路是:已經計算過的資料,就不計算了,直接複用

方案二 使用物件快取保留計算結果,方便複用

思路:

先提前定義一個物件,儲存第0項和第1項的值,後續在計算的過程中,計算一項的值,就把這個值儲存到物件當中去;如果繼續計算發現某一項的值已經存到物件當中去了,就直接用,如果沒有存到物件當中,就繼續存一份,方便後續的使用,以減少重複計算,提升效能

程式碼:

let obj = {
    0: 0, // 第0項的值為0
    1: 1 // 第1項的值為1
}

function fib(i) {
    if (i == 0) {
        return obj[0]
    }
    if (i == 1) {
        return obj[1]
    }
    /**
     * 如果i不在obj中,即i不屬於obj的key,比如i == 2
     * 就直接新增一份:obj[2] = obj[1] + obj[0]
     *       等式變換:obj[2] = fib(1) + fib(0)
     *       以此類推:obj[i] = fib(i - 1) + fib(i - 2) 
     *       這個表示式成立的條件是從i >= 2 開始,也就是i不在obj的key中
     *       所以如果不在的時候,就存一份使其在,那麼後續需要再次計算的時候
     *       就直接複用即可
     * */
    // 這是從第2項開始的。若沒有的,即之前沒計算過的,就直接存一份在物件中,方便下次複用
    if (!(i in obj)) {
        obj[i] = fib(i - 1) + fib(i - 2)
        console.log('看看obj物件中儲存的資料', obj);
        return fib(i - 1) + fib(i - 2)
    } else if (i in obj) { // 若是有的,即之前計算過的,就直接取到這個結果,直接用
        return obj[i]
    }
}

console.log('斐波那契', fib(6)); // 8 

列印obj物件截圖:

這裡也可以使用陣列去做一個快取資料,儲存一份計算過的值,這裡不贅述。思路是一樣的,大家可以自己試一下

方案三 定義變數累加到某一項

思路:

既然斐波那契數列是累加的,那麼我們們就不停的加就行了。當求:fib(6)的時候,我們們就從fib(1)開始加... 當然,要定義一個變數作為累加的容器

程式碼:

function fib(n) {
    let firstVal = 0 // 頭一項為0
    let secondVal = 1 // 第二項為1
    let thirdVal = null // 第三項先定義一個null,預留著後續的累加
    if (n == 0) {
        return firstVal
    }
    if (n == 1) {
        return secondVal
    }
    if (n >= 2) {
        for (let i = 2; i <= n; i++) {
            thirdVal = secondVal + firstVal // 這一項等於前一項加上前前一項
            /** 相當於整體往前進一位 */
            firstVal = secondVal // 把前一項的值賦值給前前一項
            secondVal = thirdVal // 把這一項的值賦值給前一項
            console.log('看看累加的值', thirdVal);
        }
        return thirdVal
    }
}
console.log('斐波那契', fib(10)); // 55

列印累加的值:

總結

好記性不如爛筆頭,記錄一下吧^_^ ,雖然前面記錄著,後面忘記著...

解決斐波那契數列的方式有很多種,比如還可以使用通項公式表示式之類的。主要是思路,在我們日常工作中,對於一些資料的校驗、以及資料架構加工,常常需要使用一點演算法的思想在裡面,這樣寫出來的程式碼,才優雅(裝X)

相關文章