前端算法 - 动态规划

239 阅读3分钟

斐波那切数列

前两位是固定的 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
}