• LeetCode 斐波那契序列


    题目描述

    写一个函数,输入 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。

    思路

    最直观的思路是递归,如下

    class Solution {
        public int fib(int n) {
            if (n <= 1) return n;
            return fib(n - 1) + fib(n - 2);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    但是递归会造成非常多的重复计算,提交会报超时。

    动态规划

    上面的递归是自顶向下求解(由求解F(n)开始,递归先求解F(n-1),…一直到F(1)),我们可以在递归的基础上加上记忆化。也可以换个角度,自底向上求解(先求解F(1),再求解F(2),最后求解F(n)),于是考虑用动态规划来做,记得加上模运算

    class Solution {
        int MOD = 1_000_000_000 + 7;
        public int fib(int n) {
            if (n <= 1) return n;
            int[] f = new int[n + 1];
            f[1] = 1;
            for (int i = 2; i <= n; i++) f[i] = (f[i - 1] + f[i - 2]) % MOD;
            return f[n];
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    动态规划的时间复杂度是 O ( n ) O(n) O(n)

    矩阵快速幂

    进阶的做法可以利用矩阵快速幂,将时间复杂度降低为 O ( l o g n ) O(logn) O(logn)
    利用矩阵乘法可以建立这样一种递推关系:
    [ 1 1 1 0 ] × [ F ( n ) F ( n − 1 ) ] = [ F ( n ) + F ( n − 1 ) F ( n ) ] = [ F ( n + 1 ) F ( n ) ] \left[

    1110
    \right ] \times \left[
    F(n)F(n1)
    \right] = \left[
    F(n)+F(n1)F(n)
    \right ] = \left[
    F(n+1)F(n)
    \right ] [1110]×[F(n)F(n1)]=[F(n)+F(n1)F(n)]=[F(n+1)F(n)]

    由此可以推出,如下的关系
    [ 1 1 1 0 ] n − 1 × [ F ( 1 ) F ( 0 ) ] = [ F ( n ) F ( n − 1 ) ] \left[

    1110
    \right ] ^{n-1} \times \left[
    F(1)F(0)
    \right] = \left[
    F(n)F(n1)
    \right ] [1110]n1×[F(1)F(0)]=[F(n)F(n1)]

    于是问题就转变成了求一个矩阵的n次幂,而求幂,我们可以用快速幂算法,将时间复杂度降低到 O ( l o g n ) O(logn) O(logn)
    只是两个整数的乘法,变成了两个矩阵的乘法。注意矩阵中,我们初始的矩阵应该设为单位矩阵
    [ 1 0 0 1 ] \left[

    1001
    \right ] [1001]
    单位矩阵和任意矩阵相乘,都等于被乘的矩阵本身。
    这个单位矩阵,就相当于整数11与任何数相乘都等于其本身。

    关于矩阵乘法,又重新复习了一下大学线性代数的基础知识。有2个矩阵A和B,A有m行,n列,称A是一个 m × n m × n m×n 的矩阵,B有n行,k列,称B是一个 n × k n × k n×k 的矩阵。只有当A的列数,等于B的行数时,这两个矩阵才能相乘,相乘得到的结果C,是一个 m × k m×k m×k 的矩阵。对于结果矩阵C中的第[i,j]个位置的值,其等于矩阵A的第i行,和矩阵B的第j列,每个元素相乘并相加的结果。

    class Solution {
    
        int MOD = 1_000_000_000 + 7;
    
        public int fib(int n) {
            if (n <= 1) return n;
            int[][] matrix = {{1, 1}, {1, 0}};
            int[][] res = pow(matrix, n - 1);
            // 由于F(1) = 1, 则最终矩阵的第一行第一列的值, 就是F(n)
            return res[0][0];
        }
    
    	// 快速幂算法
        private int[][] pow(int[][] m, int p) {
            int[][] res = {{1, 0}, {0, 1}}; // 单位矩阵, 单位矩阵乘以任何矩阵都等于它本身
            while (p > 0) {
                if ((p & 1) == 1) {
                    res = multi(res, m);
                }
                p >>= 1;
                m = multi(m, m);
            }
            return res;
        }
    	
    	// 2个矩阵相乘
        private int[][] multi(int[][] matrix1, int[][] matrix2) {
            int n1 = matrix1.length, m1 = matrix1[0].length;
            int n2 = matrix2.length, m2 = matrix2[0].length;
            if (m1 != n2) throw new IllegalArgumentException("无法计算矩阵乘法");
    
            // 矩阵乘法
            int[][] res = new int[n1][m2];
            for (int i = 0; i < n1; i++) {
                for (int j = 0; j < m2; j++) {
                    for (int k = 0; k < m1; k++) {
                        res[i][j] = (int) ((res[i][j] + ((long)matrix1[i][k] * matrix2[k][j]) % MOD) % MOD);
                    }
                }
            }
            return res;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
  • 相关阅读:
    2023年11月15号期中测验判断题(Java)
    LeetCode 343. 整数拆分(动态规划)
    【关系抽取】TPLinker:单阶段联合抽取,并解决暴漏偏差
    L2-007 家庭房产 - java
    设计模式之享元模式
    Java自动化测试框架有哪几类、区别是什么?
    Java基础 --- 终止线程 Terminate Threads
    移远EC600U-CN开发板 11.22
    适配器模式
    线程中不安全的集合
  • 原文地址:https://blog.csdn.net/vcj1009784814/article/details/125595699