斐波那契数列,一鱼四吃

603 阅读3分钟

问题简介

有一个数列被称为斐波那契数列,满足条件:

  • 第0项为0,第一项为1
  • 之后的每一项都是其前两项之和 求第n项的值

求解方法

  • 递归

    • 如果求解第0项或者第1项,直接返回
    • 如果求解第n项,返回第 n-1 项 + 第 n-2 项
class Solution {
    public int fib(int n) {
        if(n == 0 || n ==1){
            return n;
        }
        return fib(n - 1) + fib(n - 2);
    }
}
  • 迭代

从第0项开始求到第n项,用临时变量保存前两项 (溢出按题目要求取余)

class Solution {
    public int fib(int n) {
        if(n == 0 || n == 1){
            return n;
        }
        int fib0 = 0;
        int fib1 = 1;
        int tmp = 0;
        for(int i = 2; i <= n; i++){
            tmp = fib0;
            fib0 = fib1;
            fib1 = (fib0 + tmp)%(1000000007);
        }
        return fib1;
    }
}
  • 动态规划

用一个长度为n + 1的数组,也是按照前面迭代的方式逐个求解,最后返回dp[n]

class Solution {
    public int fib(int n) {

        int[] dp = new int[n + 1];
        for(int i = 0 ; i <= n; i++){
            if(i == 0 || i == 1){
                dp[i] = i;
            }else{
                dp[i] =  (dp[i - 1] + dp[i - 2]) % (1000000007);
            }
        }
        return dp[n];
    }
}
  • 矩阵快速幂

我自己是无论如何都想不到能这样解,稍微看懂了一些

第一个式子可以简单地通过矩阵乘法每个位置的计算验证,其实是f(n) = f(n - 1) + f(n - 2)的矩阵乘法的形式的杂糅体现

通过第一个式子我们只是将问题单纯复杂化了,并没有缩小时间的复杂度,但它是在为后面的幂的分解做铺垫

a就是,求解其n次幂可以转化为求 (a^(n/2))^2,这里要注意奇数和偶数的微妙的不同,这个思路很容易用递归实现

class Solution {
    public int fib(int n) {
        if(n == 0 || n == 1){
            return n;
        }

        long[][] last = getMatrixAt(n - 1);
        return (int)last[0][0];
    }

    public long[][] getMatrixAt(int n){
        long[][] orign = new long[2][2];
        orign[0][0] = 1;
        orign[0][1] = 1;
        orign[1][0] = 1;
        orign[1][1] = 0;

        long[][] last;

        if(n == 1){
            last = orign;
        }else if( n % 2 == 0){
            last = matrixMul(getMatrixAt(n/2),getMatrixAt(n/2));
        }else{
            last = matrixMul( matrixMul(getMatrixAt((n - 1)/2),getMatrixAt((n - 1)/2) ),orign);
        }

        return last;
    }

    public long[][] matrixMul(long[][] matrix1,long[][] matrix2){
        long[][] matrix = new long [2][2];
        matrix[0][0] = ((matrix1[0][0] * matrix2[0][0])% (1000000007) + (matrix1[0][1] * matrix2[1][0]) % (1000000007)) % (1000000007) ;
        matrix[0][1] = ((matrix1[0][0] * matrix2[0][1])% (1000000007) + (matrix1[0][1] * matrix2[1][1]) % (1000000007)) % (1000000007);
        matrix[1][0] = ((matrix1[1][0] * matrix2[0][0])% (1000000007) + (matrix1[1][1] * matrix2[1][0]) % (1000000007)) % (1000000007);
        matrix[1][1] = ((matrix1[1][0] * matrix2[0][1])% (1000000007) + (matrix1[1][1] * matrix2[1][1]) % (1000000007)) % (1000000007);

        return matrix;
    }
}

时间复杂度分析

  • 直接递归是4种当中最慢的那种,展开是二叉树的结构,复杂度为O(n^2)
  • 直接迭代是O(n),一路求,一遍过
  • 动态规划也是O(n),但比起直接迭代需要的空间更多
  • 矩阵快速幂最快,达到了O(logn),是不断二分到 n==2时的矩阵