[剑指-Offer] 10. 斐波那契数列、青蛙跳台阶问题(递归、fib快速计算、fib数列求和公式、fib数列通项公式)

1. 题目来源

链接:用两个栈实现队列
来源:LeetCode——《剑指-Offer》专项

2. 题目说明

写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项。斐波那契数列的定义如下:

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

斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。

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

示例 :

输入:n = 2
输出:1

示例 2:

输入:n = 5
输出:5

提示:

  • 0 <= n <= 100

3. 题目解析

3.1 方法一:自下而上、循环求解

做好 % 的运算就行了,否则会爆掉

// 执行用时 :8 ms, 在所有 C++ 提交中击败了61.41%的用户
// 内存消耗 :8 MB, 在所有 C++ 提交中击败了100.00%的用户

class Solution {
public:
    int fib(int n) {
        long long  a = 0, b = 1, c = 0;
        if (n == 0)
            return 0;
        if (n == 1)
            return 1;
        while (--n) {
            c = a % 1000000007 + b % 1000000007;
            a = b;
            b = c;
        } 
        return c % 1000000007;
    }
};

3.2 青蛙跳台阶、经典递归

n 级台阶时的跳法看成是 n 的函数,记为 f(n)。当 n>2 时,第一次跳的时候就有两种不同的选择

  • 一是第一次只跳 1 级,此时跳法数目等于后面剩下的 n-1 级台阶的跳法数目,即为 f(n-1)
  • 另外一种选择是第一次跳 2 级,此时跳法数目等于后面剩下的 n-2 级台阶的跳法数目,即为 f(n-2)

因此n级台阶的不同跳法的总数 f(n)=f(n-1)+f(n- 2)

// 执行用时 :4 ms, 在所有 C++ 提交中击败了61.72%的用户
// 内存消耗 :8.2 MB, 在所有 C++ 提交中击败了100.00%的用户
class Solution {
public:
    int numWays(int n) {
        long long arr[2] = {1, 1};
        if (n < 2)
            return arr[n];
        long long  a = 1, b = 1, c = 0;
        while (--n) {
            c = a % 1000000007 + b % 1000000007;
            a = b;
            b = c;
        } 
        return c % 1000000007;
    }
};

3.3 fib数列 O ( l o g n ) O(logn) 计算法

以前做题遇到过,没深究,再次遇到了,总结一下:

O ( l o g n ) O(logn) 算法:由于这种算法需要用到一个很生僻的数学公式,因此很少有面试官会要求我们掌握。不过以防不时之需,我们还是简要介绍一下这种算法。在介绍这种方法之前,我们先介绍一个数学公式:
[ f ( n ) f ( n 1 ) f ( n 1 ) f ( n 2 ) ] = [ 1 1 1 0 ] n 1 \begin{bmatrix} f(n) & f(n-1) \\ f(n-1) & f(n-2) \end{bmatrix}\quad=\begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix}^{n-1}\quad

这个公式用数学归纳法不难证明,在此不作证明
了这个公式,我们只需要求得矩阵 [ 1 1 1 0 ] n 1 \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix}^{n-1}\quad 即可得到 f(n)。现在的问题转为如何求矩阵的乘方。如果只是简单地从 0 开始循环,n 次方需要 n次运算,那其时间复杂度仍然是 O ( n ) O(n) ,并不比前面的方法快。但我们可以考虑乘方
的如下性质:
a n = { a n / 2 a n / 2 n a ( n 1 ) / 2 a ( n 1 ) / 2 n a^n=\left\{ \begin{aligned} {a^{n/2}}\cdot a^{n/2} && n为偶数\\ a^{(n-1)/2}\cdot a^{(n-1)/2} & & n为奇数 \\ \end{aligned} \right.

从上面的公式我们可以看出,想求得 n 次方,就要先求得n/2 次方,再把 n/2 次方的结果平方一下即可。这可以用递归的思路实现。可以脑补递归树。

3.4 斐波拉契数列通项公式与求和公式

这个数列从第三项开始,每一项都等于前两项之和。它的通项公式为:

a n = 1 5 [ ( 1 + 5 2 ) n ( 1 5 2 ) n ] a_n = \frac1{\sqrt5}\left[ \left( \frac{1+{\sqrt5}}{2} \right)^n-\left( \frac{1-{\sqrt5}}{2} \right)^n \right]

又叫 “比内公式”,是用无理数表示有理数的一个范例。n 趋于无穷大时,前后两项比约等于黄金比率。

斐波那契数列求和公式: S n = 2 a n + a n 1 1 S_n=2a_n+a_{n-1}-1
文字说明:数列的最后一项的两倍加上倒数第二项再减去1

证明过程简单贴在下面:
在这里插入图片描述

发布了307 篇原创文章 · 获赞 125 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/yl_puyu/article/details/104434155