【剑指offer】斐波那契数列

落爺英雄遲暮 提交于 2020-02-04 13:14:33

题目描述

大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。n<=39

下面首先会简单介绍递归算法,然后给出递归算法的解法代码,以及使用非递归算法的解决代码,最后是简单使用动态规划的方法解决。

递归算法

斐波那契数列是典型的递归代表。递归算法的核心在于两点:

  1. 结束递归的边界条件
  2. 递归公式

递归公式简单理解起来,就是类似于我们高中数学里可以用式子表达的数列,类似于初中学的找前后的规律,只是把这个规律公式化了。

递归算法的两个核心要点,对应到代码算法的实现,也就是以下 3 步:

  1. 找整个递归的终止条件:递归应该在什么时候结束?
  2. 找返回值:应该给上一级返回什么信息?
  3. 本级递归应该做什么:这一级递归中,要完成什么任务?

举个例子:假设有个数列 1 3 5 7 9 11 … 找到第n项的值。在这个数列中,开始的一项是 1,是无规律可循的一项,这一项到数列的尽头了,是 “结束” 项;此后,每一项都是在其前一项的基础上加 2,得到的。因此可以写为:

  1. 边界条件:f(1) = 1
  2. 递归公式:f(n) = f(n-1)+2

找到上述的两个要点,就可以实现递归算法。

解题思路

这一道题中,要求返回斐波那契数列的第 n 项,从 0 开始。
斐波那契数列:0 1 1 2 3 5 8 13 …
可以知道其边界条件和递归公式分别为:

  1. fibonacci(1) = 0, fibonacci(1) = 1,fibonacci(2) = 1;
  2. fibonacci(n) = fibonacci(n-1)+fibonacci(n-2)

实现代码:

class Solution {
public:
    int Fibonacci(int n) {
        if(n==0){
            return 0;
        }
        else if(n==1 || n==2){
            return 1;
        }
        else{
           return Fibonacci(n-1)+Fibonacci(n-2);
        }
    }
};

其实,递归算法的效率不一定很高,而且消耗的存储空间也不少,但是用递归算法有时候实现代码会更简洁。上述算法的时间复杂度为 O(2n)O(2^{n})

当然,这道题不用递归也可以解决。我们在算数列中的某一项时,也不会采用递归这种方式,而是会从第一项开始算起,由于斐波那契数列的递归公式计算 F(n) 时,需要用到 F(n-1) 和 F(n-2),所以利用中间变量记录这两项就可以了。

实现代码:

class Solution {
public:
    int Fibonacci(int n) {
        if(n==0 || n==1){
            return n;
        }
        int a=0,b=1; // a代表F(n-2), b代表F(n-1)
        int res = 0;
        for(int i=2;i<=n;i++){
            res = a + b;
            a = b;
            b = res;
        }
        return res;
    }
};

这个方法空间复杂度只需要 O(1)O(1),时间复杂度为 O(n)O(n)

在牛客网的讨论区里还有人使用了动态规划解决这道题。

什么是动态规划?
动态规划方法一般用来求解最优化问题。动态规划应用于子问题重叠的情况,即不同的子问题具有公共的子子问题(子问题的求解是递归进行的,将其划分为更小的子子问题)。

动态规划包含哪些步骤?
解决动态规划问题一般分为四步:

  1. 定义一个状态,这是一个最优解的结构特征
  2. 进行状态递推,得到递推公式
  3. 进行初始化
  4. 返回结果

按照上述步骤,分析这一题:

  1. 状态:f(i)表示第i个斐波那契数
  2. 初始化:f(0)=0, f(1)=1, f(2)=1
  3. 状态递推:f(i) = f(i-1)+f(i-2)
  4. 返回结果 f(n)

实现起来其实和上面的非递归算法差不多,为了更加简洁,使用 f 代表 f(i-2),使用 g 代表 f(i-1),状态递推等于 g+f,而 g+f 会成为下一次计算的 f(i-1),也就是 g,所以再次赋给 g,g’=g+f;而 f 应该更新为原来的 g,也等于g’-f,所以有 f = g-f。

实现代码:

class Solution {
public:
    int Fibonacci(int n) {
        if(n<=0) return 0;
        int f = 0, g = 1;
        while(n--) {
            g += f;
            f = g - f;
        }
        return f;
    }
};

参考:

  1. https://www.nowcoder.com/profile/2126927/codeBookDetail?submissionId=17443716
  2. https://segmentfault.com/a/1190000019613597
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!