前端演算法 - 動態規劃

JsRicardo發表於2019-12-14

斐波那切數列

前兩位是固定的 1, 1 第三位開始,當前位是前兩位相加的和

1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...

面試題解:

給出第n位,問第n位的值為多少

class Solution {
    /**
     * 迴圈思想
     * 傳統斐波那切數列解法
     * @param n 第幾位
     * @returns { Number } 第幾位的值
     */
    public static classicFib(n: number): number {
        if (n <= 1) { return n; }

        let a = 1, b = 1, c;

        for (let i = 3; i <= n; i++) {
            c = a + b;
            a = b;
            b = c;
        }
        return c;
    }
    /**
     * 遞迴思想
     * 利用斐波那切數列的規則,第n位的值 = 第n-1位 + 第n-2位
     * @param n 第幾位
     */
    public static newFib(n: number): number {
        if (n <= 1) { return n; }
        return this.newFib(n - 1) + this.newFib(n - 2);
    }
}
複製程式碼

LeetCode演算法題解

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)。

這道題是典型的動態規劃問題

  • 什麼是動態規劃:動態規劃就是 遞迴 + 記憶化

在遞迴的時候,經常會進行很多重複的計算,導致時間複雜度是指數級的。

動態規劃時,在遞迴的時候,快取上一些可以重複利用的值

斐波那切數列

從上圖可以看出:

  • 計算fb(5) 需要計算 fb(4) + fb(3)
  • 計算fb(4) 需要計算 fb(3) + fb(2)
  • 計算fb(3) 需要計算 fb(2) + fn(1)
  • ...

中間我們重複計算了很多次fb(3) fb(2) fb(1)

計算的數越大 那麼重複計算的次數也就越多, 若果把計算過的數存起來,可以節約很多計算量

class Memoize {
    // 快取已經計算的斐波那切數
    private static cache: number[] = [0, 1]

    /**
     * 自頂向下的 動態規劃 解 斐波那切數
     * @param n 第幾位數
     */
    public static memoize(n: number) {
        if (n < 1) return n;
        // 如果已經快取就取快取
        if (this.cache[n] !== undefined) return this.cache[n];
        // 沒有快取就計算出來快取進去
        this.cache[n] = this.memoize(n - 1) + this.memoize(n - 2);

        return this.cache[n]
    }
}
複製程式碼

動態規劃

經典試題:青蛙跳臺階問題

一隻青蛙一次可以跳上1級臺階,也可以跳上2級臺階。求該青蛙跳上一個n級的臺階總共有多少種跳法?

  • 如果這隻青蛙跳上了第N級臺階,那麼最後一次跳躍之前,它一定在第 N-1 級 或者第 N-2級臺階上。
  • 那麼跳上第N級臺階有多少種跳法就變成了兩個子問題
  • 跳上第 N-1 級臺階的跳法 加上 跳上第 N-2 級的跳法
  • 迴圈

f(n) = f(n -1) + f(n -2)

可以看出 青蛙跳臺階問題 就是一種斐波那契數列問題

// 青蛙跳臺階問題
function jump(n: number): number {
    if (n <= 0) return -1;
    if (n === 1) return 1;
    if (n === 2) return 2;
    return jump(n - 1) + jump(n - 2);
}
複製程式碼

青蛙跳臺階的變型版

一隻青蛙一次可以跳上1級臺階,也可以跳上2級臺階,或者N級臺階。求該青蛙跳上一個N級的臺階總共有多少種跳法?

  • 可以從 n-1 跳到 n
  • 可以從 n-2 跳到 n
  • 可以從 n-3 跳到 n
  • ...
  • 也可以從 0 直接跳到 n

所以 fn = f(n -1) + f(n - 2).....+ f(1) + f(0)

// 青蛙跳臺階的變型版
function jump2(n: number): number {
    if (n <= 0) return -1;
    if (n === 1) return 1;
    if (n === 2) return 2;

    let result = 0;

    for (let i = 1; i < n; i++) { // 跳的時候至少要跳一級臺階,所以從 1 開始
        result += jump(n - i);
    }
    return result + 1; // 從0級直接跳到n級 所以+1
}
複製程式碼

相關文章