剑指Offer 10-Ⅰ.斐波那契数列(C++)

前言

利用「尾递归」、「动态规划」、「递推」和「矩阵快速幂」来解决问题。

剑指Offer 10-Ⅰ.斐波那契数列

问题描述

写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:
F(0) = 0,   F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 01 开始,之后的斐波那契数就是由之前的两数相加而得出。
答案需要取模 1e9+71000000007),如计算初始结果为:1000000008,请返回 1

示例

|| 输入:n = 2
|| 输出:1

尾递归

一开始直接用普通递归做,n稍微大一点的时候就超出时间限制了,于是想到「尾递归」

  • 递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储,因此递归次数过多容易造成栈溢出。
    3递归.png

  • 尾递归是把当前的运算结果放在参数里传递给下层函数,只用保存最后一个函数堆栈。
    3尾递归.png

代码

class Solution { 
public:
    int fib(int n) {
        return fibTail(n,0,1);
    }
    int fibTail(int n,int curr,int next){
        int mod = 1e9+7;
        if(n==0)
            return curr;
        return fibTail(n-1,next%mod,(curr+next)%mod);
    }
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

动态规划

题中已经给出了状态转移方程
F(N) = F(N - 1) + F(N - 2)
与n值对应,n是从0开始的,我们申请一个大小为 n+1 的数组 dp,
定义 dp[i] 为斐波那契数列第 i 项的值,
那么 dp[n] 为斐波那契数列第 n 项的值,
dp[0]=0 和 dp[1]=1 为两个初始状态,
利用dp[i] = dp[i-1] + dp[i-2]循环计算dp[i]的值。

代码

class Solution { 
public:
    int fib(int n) {
        if(n<=1)
            return n;
        int mod = 1e9+7;
        int dp[n+1];
        dp[0] = 0;
        dp[1] = 1;
        for(int i=2;i<=n;++i){
            dp[i] = dp[i-1] + dp[i-2];
            dp[i] %= mod;
        }
        return dp[n];
    }
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

递推

根据 F(N) = F(N - 1) + F(N - 2),我们知道 F(N) 的值只与 F(N - 1) 和 F(N - 2) 有关,那就可以用递推来实现动态规划,也就不需要申请一个数组来保存过程中的值,我们只申请三个常量即可。

代码

class Solution { 
public:
    int fib(int n) {
        if(n<=1)
            return n;
        int mod = 1e9+7;
        int a = 0,b = 1;
        int sum = a + b;
        for(int i=2;i<n;++i){
            a = b;
            b = sum;
            sum = (a + b)%mod;
        }
        return sum;
    }
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

矩阵快速幂

本题可以根据构造合适的矩阵乘法来实现 F(N) = F(N - 1) + F(N - 2) 中 F(N)、F(N - 1) 和 F(N - 2) 之间的依赖关系,利用 F(N - 1) 和 F(N - 2) 推导出 F(N) 。
根据矩阵乘法构造出
[ F ( n ) F ( n − 1 ) ] = [ F ( n − 1 ) + F ( n − 2 ) F ( n − 1 ) ] = [ 1 1 1 0 ] × [ F ( n − 1 ) F ( n − 2 ) ] \left[ \begin{array}{l} F(n)\\ F(n - 1) \end{array} \right] = \left[ \begin{array}{l} F(n - 1) + F(n - 2)\\ F(n - 1) \end{array} \right] = \left[ {\begin{matrix} 1&1\\ 1&0 \end{matrix}} \right] \times \left[ \begin{array}{l} F(n - 1)\\ F(n - 2) \end{array} \right] [F(n)F(n1)]=[F(n1)+F(n2)F(n1)]=[1110]×[F(n1)F(n2)]

m a t = [ 1 1 1 0 ] mat = \left[ {\begin{matrix} 1&1\\ 1&0 \end{matrix}} \right] mat=[1110]
我们已知的初始状态为
[ F ( 0 ) F ( 1 ) ] \left[ \begin{array}{l} F(0)\\ F(1) \end{array} \right] [F(0)F(1)]

推出
[ F ( n ) F ( n − 1 ) ] = m a t × m a t × ⋯ m a t × [ F ( 0 ) F ( 1 ) ] = m a t × m a t × ⋯ m a t × [ 0 1 ] = m a t n − 1 × [ 0 1 ] \left[ \begin{array}{l} F(n)\\ F(n - 1) \end{array} \right] = mat \times mat \times \cdots mat \times \left[ \begin{array}{l} F(0)\\ F(1) \end{array} \right] = mat \times mat \times \cdots mat \times \left[ \begin{array}{l} 0\\ 1 \end{array} \right] = ma{t^{n - 1}} \times \left[ \begin{array}{l} 0\\ 1 \end{array} \right] [F(n)F(n1)]=mat×mat×mat×[F(0)F(1)]=mat×mat×mat×[01]=matn1×[01]
所以我们可以利用快速幂求出
m a t n − 1 ma{t^{n - 1}} matn1
直接循环累乘也可以求出来,但是时间复杂度为O(n),而快速幂算法每一步都把指数分成两半,用相应的底数做平方运算,所需要执行的循环次数也变小,时间复杂度为O(log n)。

代码

class Solution { 
public:
    int mod = 1e9+7;
    vector<vector<long>> mul(vector<vector<long>>& a, vector<vector<long>>& b){
        int arow = a.size();    //a的行数
        int brow = b.size();    //b的行数
        int bcol = b[0].size(); //b的列数
        vector<vector<long>> ans(arow,vector<long>(bcol,0));    //arow行bcol列,初始化为0
        for(int i=0;i<arow;++i){
            for(int j=0;j<bcol;++j){
                for(int k=0;k<brow;++k){
                    ans[i][j] += a[i][k] * b[k][j];
                    ans[i][j] %= mod;
                }
            }
        }
        return ans;
    }
    int fib(int n) {
        if (n <= 1) 
            return n;
        vector<vector<long>> mat = {{1, 1},
                                    {1, 0}};
        vector<vector<long>> ans = {{1},
                                    {0}};
        int x = n - 1;
        while (x != 0) {
            if ((x & 1) != 0) ans = mul(mat, ans);
            mat = mul(mat, mat);
            x >>= 1;
        }
        return ans[0][0] % mod;
    }
};
  • 时间复杂度:O(log n)
  • 空间复杂度:O(1)

微信公众号文章

剑指Offer 10-Ⅰ.斐波那契数列

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dululuya

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值