LeetCode 剑指 Offer 10-1/2 斐波那契数列、青蛙跳台阶[递归/动态规划]

515 阅读4分钟

这是我参与更文挑战的第 5 天,活动详情查看: 更文挑战

斐波那契数列

题目描述

写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:

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

斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

示例 1:

输入:n = 2
输出:1

示例 2:

输入:n = 5
输出:5

提示

  • 0 <= n <= 100

思路分析

斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。感觉这种题目就是为递归而生的,大问题分解为多个子问题,子问题和父问题的解法逻辑一直,因为是从 0 和 1 开始,所以 0 和 1 就是终止条件。

递归解法我们可以使用备忘录解法进行优化,将计算过的值存入到数组中,如果已经计算过该表达式的值,直接从数组中取值即可。

同时我们也可以使用动态规划解法,动态的改变几个临时变量的值,优化计算时间。

代码展示

解法一:时间复杂度是O(n){O(n)},空间复杂度是O(n){O(n)}

int[] mem = new int[101];
public int fib(int n) {
		return fib_r(n);
}
public int fib_r(int n) {
        if (n == 0){
            return 0;
        }
        if (n <= 2){
            return 1;
        }
        if (mem[n] != 0){
            return mem[n];
        }
        mem[n] = (fib_r(n-1) + fib_r(n-2)) % 1000000007;
        return mem[n];

    }

解法二:时间复杂度是O(n){O(n)},空间复杂度是O(1){O(1)}

    public int fib(int n) {
        if (n == 0){
            return 0;
        }
        if (n <= 2){
            return 1;
        }
        int a = 1;
        int b = 1;
        int c = 0;
        for (int i = 3; i <= n; i++) {
            c = (a + b) % 1000000007;
            a = b;
            b = c;
        }
        return c;
    }

a,b,c 的值在每次循环后进行一次变换,最终得到最后的值。

青蛙跳台阶问题

题目描述

一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

示例 1:

输入:n = 2
输出:2

示例 2:

输入:n = 7
输出:21

提示

  • 0 <= n <= 100

思路分析

青蛙每次可以跳上1级台阶,也可以跳上2级台阶,也就是说只有这两种跳法,比如输入 n = 3,可以分解为连续跳三次一级台阶和两次(一次跳一级台阶,一次跳二级台阶,顺序不同,跳法不同),所以一共有 3 中跳法。n 可以分解为 n -1 和 n - 2 解法的和。

所以我们也就推出了我们的递归公式,终止条件是 n = 1 和 n = 2。需要注意的是 n = 0 是,返回值为 1,也就是说什么都不用跳,直接返回。

递归解法我们可以使用备忘录解法进行优化,将计算过的值存入到数组中,如果已经计算过该表达式的值,直接从数组中取值即可。

第二种解法是动态规划,我们可以动态地改变几个临时变量的值,大大地优化计算时间。

代码展示

解法一:时间复杂度是O(n){O(n)},空间复杂度是O(n){O(n)}

int[] cache = new int[101];
    public int numWays(int n) {
        if (n == 0){
            return 1;
        }
        if (n == 1){
            return 1;
        }
        if (n == 2){
            return 2;
        }
        if (0 != cache[n]){
            return cache[n];
        }
        cache[n] = (numWays(n-1) + numWays(n-2)) % 1000000007;
        return cache[n];
    }

解法二:时间复杂度是O(n){O(n)},空间复杂度是O(1){O(1)}

    public int numWays(int n) {
        if (n == 0){
            return 1;
        }
        if (n == 1){
            return 1;
        }
        if (n == 2){
            return 2;
        }
        int a = 1;
        int b = 2;
        int c = 0;
        for (int i = 3; i <=n ; i++) {
            c = (a + b) % 1000000007;
            a = b;
            b = c;
        }
        return c;
    }

a,b,c 的值在每次循环后进行一次变换,最终得到最后的值。

总结

斐波那契数列和青蛙跳台阶是典型的递归题目,解题者很容易想到递归解法,通过备忘录解法进行优化,同时我们也可以动态规划进行解答该题,不断的变化变量的值,返回最终的值。最后还要进行一个最大值越界处理。