首页 > 其他分享 >力扣-LCR 126. 斐波那契数

力扣-LCR 126. 斐波那契数

时间:2024-04-26 19:23:35浏览次数:18  
标签:斐波 LCR Matrix int 复杂度 cache 力扣 fib return

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

标签:斐波,LCR,Matrix,int,复杂度,cache,力扣,fib,return
From: https://www.cnblogs.com/trmbh12/p/18160302

相关文章

  • TODO-力扣-707. 设计链表
    1.题目题目地址(707.设计链表-力扣(LeetCode))https://leetcode.cn/problems/design-linked-list/题目描述你可以选择使用单链表或者双链表,设计并实现自己的链表。单链表中的节点应该具备两个属性:val和next。val是当前节点的值,next是指向下一个节点的指针/引用。如果......
  • 力扣-118. 杨辉三角
    1.题目介绍题目地址(118.杨辉三角-力扣(LeetCode))https://leetcode.cn/problems/pascals-triangle/题目描述给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。在「杨辉三角」中,每个数是它左上方和右上方的数的和。 示例1:输入:numRows=5输出:[[1],......
  • 力扣-442. 数组中重复的数据
    1.题目介绍题目地址(442.数组中重复的数据-力扣(LeetCode))https://leetcode.cn/problems/find-all-duplicates-in-an-array/题目描述给你一个长度为n的整数数组nums,其中nums的所有整数都在范围[1,n]内,且每个整数出现一次或两次。请你找出所有出现两次的整数,......
  • 力扣-448. 找到所有数组中消失的数字
    1.题目题目地址(448.找到所有数组中消失的数字-力扣(LeetCode))https://leetcode.cn/problems/find-all-numbers-disappeared-in-an-array/题目描述给你一个含n个整数的数组nums,其中nums[i]在区间[1,n]内。请你找出所有在[1,n]范围内但没有出现在nums中的数字,......
  • 力扣-645. 错误的集合
    1.题目介绍题目地址(645.错误的集合-力扣(LeetCode))https://leetcode.cn/problems/set-mismatch/题目描述集合s包含从1到 n 的整数。不幸的是,因为数据错误,导致集合里面某一个数字复制了成了集合里面的另外一个数字的值,导致集合丢失了一个数字并且有一个数字重复。......
  • 力扣-697. 数组的度
    1.题目介绍题目地址(697.数组的度-力扣(LeetCode))https://leetcode.cn/problems/degree-of-an-array/题目描述给定一个非空且只包含非负数的整数数组 nums,数组的度的定义是指数组里任一元素出现频数的最大值。你的任务是在nums中找到与 nums 拥有相同大小的度的最短......
  • 力扣-396. 旋转函数
    1.题目介绍题目地址(396.旋转函数-力扣(LeetCode))https://leetcode.cn/problems/rotate-function/题目描述给定一个长度为n的整数数组 nums 。假设 arrk 是数组 nums 顺时针旋转k个位置后的数组,我们定义 nums 的旋转函数  F 为:F(k)=0*arrk[0]+1*......
  • 力扣-189. 轮转数组
    1.题目题目地址(189.轮转数组-力扣(LeetCode))https://leetcode.cn/problems/rotate-array/题目描述给定一个整数数组nums,将数组中的元素向右轮转k 个位置,其中 k 是非负数。 示例1:输入:nums=[1,2,3,4,5,6,7],k=3输出:[5,6,7,1,2,3,4]解释:向右轮转1步......
  • 力扣-414. 第三大的数
    1.题目题目地址(414.第三大的数-力扣(LeetCode))https://leetcode.cn/problems/third-maximum-number/题目描述给你一个非空数组,返回此数组中第三大的数。如果不存在,则返回数组中最大的数。 示例1:输入:[3,2,1]输出:1解释:第三大的数是1。示例2:输入:[1,2]输出:2......
  • 力扣-495. 提莫攻击
    1.题目题目地址(495.提莫攻击-力扣(LeetCode))https://leetcode.cn/problems/teemo-attacking/题目描述在《英雄联盟》的世界中,有一个叫“提莫”的英雄。他的攻击可以让敌方英雄艾希(编者注:寒冰射手)进入中毒状态。当提莫攻击艾希,艾希的中毒状态正好持续 duration秒。正......