力扣-LCR 126. 斐波那契数

1.题目

题目地址(LCR 126. 斐波那契数 - 力扣(LeetCode))

https://leetcode.cn/problems/fei-bo-na-qi-shu-lie-lcof/

题目描述

斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 01 开始,后面的每一项数字都是前面两项数字的和。也就是:

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

给定 n ,请计算 F(n)

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

 

示例 1:

输入:n = 2
输出:1
解释:F(2) = F(1) + F(0) = 1 + 0 = 1

示例 2:

输入:n = 3
输出:2
解释:F(3) = F(2) + F(1) = 1 + 1 = 2

示例 3:

输入:n = 4
输出:3
解释:F(4) = F(3) + F(2) = 2 + 1 = 3

 

提示:

  • 0 <= n <= 100

 

2.题解

首先注意下, C++中取模运算%, 要求计算两边均是整数类型, 如果是float,double类型, 就会报错!!!!
这里已经给出了转移方程, 我们直接使用递归或者递推实现动态规划即可

2.1 递归实现动态规划

思路

斐波那契数列,经典递归题目,但是这里会出现超出时间限制的问题.如下图,会存在大量重复计算!!!

所以我们采用记忆化搜索的问题进行操作简化!
所谓记忆化搜索,就是将已经计算过的结果进行存储,如果已经计算过了,就不再次进行递归计算了.
比如像 fib(6) = fib(5) + fib(4), fib(5)递归计算出了fib(4),....; 后面那个fib(4)就不需要计算了

注意C++中类内是不能初始化静态数组的,必须在类外进行初始化,所以我们要不然在类外定义数组,要不然将数组的初始化放在类外!!!!
这一点跟Java不一样,一定要注意.

代码

  • 语言支持:C++

C++ Code:
(TTL,超出时间限制)TTL代码如下:


class Solution {
public:
    int fib(int n) {
        if (n < 2) return n;
        return (fib(n-1) + fib(n-2)) % static_cast<int>(1e9+7);
    }
};

优化后代码如下:

int cache[110];
class Solution {
public:
    int fib(int n) {
        if(n < 2) return n;
        if(cache[n] != 0) return cache[n];
        cache[n] = (fib(n-1) + fib(n-2)) % static_cast<int>(1e9+7);
        return cache[n];
    }
};

Java 可以实现类内静态数组初始化

class Solution {
    static int mod = (int)1e9+7;
    static int N = 110;
    static int[] cache = new int[N];
    public int fib(int n) {
        if (n <= 1) return n;
        if (cache[n] != 0) return cache[n];
        cache[n] = fib(n - 1) + fib(n - 2);
        cache[n] %= mod;
        return cache[n];
    }
}

复杂度分析

令 n 为数组长度。

  • 时间复杂度:\(O(n)\)
  • 空间复杂度:\(O(n)\)

2.2 递推实现动态规划

思路

这里由于我只需要最终结果,所以只需要存储三个元素即可,将空间复杂度简化到O(1)
但是注意这里计算时next = (prev + cur) % static_cast(1e9+7); 在这里就要进行取模运算了, 不然使用int存储的next可能会溢出
有着 0 <= n <= 100, 而其实计算到46附近时, int类型就溢出了, 所以这里使用了一个取模运算

代码

class Solution {
public:
    int fib(int n) {
        int prev = 0, cur = 1, next = 1;
        if(n <= 1) return n;
        for(int i = 2; i <= n; i++){
            int temp = next;
            next = (prev + cur) % static_cast<int>(1e9+7);
            temp = cur;
            cur = next;
            prev = temp;
        }
        return next;
    }
};

复杂度分析

令 n 为数组长度。

  • 时间复杂度:\(O(n)\)
  • 空间复杂度:\(O(1)\)

2.3 打表

思路

n较小, 我们可以直接使用方法二中的思路现将所有cache[n]初始化, 之后直接要哪个调用哪个即可.
这里有一个很难受的问题, 就是C++中并没有像是Java中的那种静态代码块, 不然打表初始化使用static{}就能解决
这里只能定义一个静态初始化函数, 里面存储静态数组和静态flag标志, 除了第一次进行打表操作外, 其余时刻initialized=true, 直接返回结果!
这里函数定义了静态变量, 也就必须是static类型!!!

代码

class Solution {
public:
    static const int mod = 1000000007;
    // 静态成员函数 cache(), 这个函数返回一个指向静态数组的指针
    static int* init() {
        static int cache[110];
        static bool initialized = false; // 使用一个静态flag标志, 决定是否进行初始化
        if (!initialized) {
            initialized = true;
            cache[0] = 0;
            cache[1] = 1;
            const int mod = 1000000007;
            for (int i = 2; i < 110; i++) {
                cache[i] = (cache[i - 1] + cache[i - 2]) % mod;
            }
        }
        return cache;
    }
    int fib(int n) {
        return init()[n];
    }
};

复杂度分析

令 n 为数组长度。

  • 时间复杂度:\(O(n)\)
  • 空间复杂度:\(O(1)\)

2.4 矩阵快速幂

思路

将这里面的递归运算转换为重复的矩阵幂运算,然后使用矩阵快速幂定义解决问题
矩阵快速幂定义

代码

using ll = long long;
const int m = 2;
const int MOD = 1e9 + 7;

class Matrix {
public:
    // 定义一个二维数组vector
    vector<vector<int>> a;

    Matrix() {
        // 初始化二维数组vector为m*m的零矩阵
        a.assign(m, vector<int>(m, 0));
    }

    Matrix(vector<vector<int>>& b) {
        a.assign(b.begin(), b.end());
    }

    void init() {
        a[0][0] = a[1][1] = 1;
        a[0][1] = a[1][0] = 0;
    }
/**
    Matrix operator*(const Matrix& other) const {
        Matrix ans;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < m; j++) {
                for (int k = 0; k < m; k++) {
                    ans.a[i][j] += (ll)a[i][k] * other.a[k][j] % MOD; // 这里前面的乘法运算就会超出int范围,所以强制转换ll类型; 为何还要%MOD呢?因为int存不下,会进行截取,所以我们只能先%MOD  
                }
                ans.a[i][j] %= MOD;
            }
        }
        return ans;
    }
**/
    // 针对这一题可以直接进行简化, 因为知道必定是2*2矩阵, 所以一个MOD即可!
    Matrix operator*(const Matrix& other) const {
        Matrix ans;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < m; j++) {
                ans.a[i][j] = (a[i][0] * other.a[0][j] + a[i][1] * other.a[1][j]) % MOD;
            }
        }
        return ans;
    }
    Matrix operator^(int n) const {
        Matrix ans;
        ans.init();
        Matrix A = *this; // 复制一份用于自乘
        while (n) {
            if (n & 1) {
                ans = ans * A;
            }
            A = A * A;
            n >>= 1;
        }
        return ans;
    }
};

class Solution {
public:
    int fib(int n) {
        if (n < 2) return n;
        vector<vector<int>> b = {{1, 1}, {1, 0}};
        Matrix ma(b);
        ma = ma ^ (n - 1);
        return ma.a[0][0]; // 这里给的幂是n-1,F(n)在上面,结果实际上是 a[0][0] * F(1) + a[0][1] * F(0) 但是 F[1] = 1, F[0] = 0;
    }
};

复杂度分析

令 n 为数组长度。

  • 时间复杂度:\(O(logn)\)
  • 空间复杂度:\(O(1)\)
posted @ 2024-04-26 19:10  DawnTraveler  阅读(8)  评论(0编辑  收藏  举报