• 作者: 负雪明烛
  • id: fuxuemingzhu
  • 个人博客:http://fuxuemingzhu.cn/
  • 个人微信公众号:负雪明烛

题目地址:https://leetcode-cn.com/problems/fei-bo-na-qi-shu-lie-lcof/

题目描述

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

示例 1:

输入:n = 2
输出:1

示例 2:

输入:n = 5
输出:5

提示:

  • 0 <= n <= 100

解题方法

递归

求第 n 个数的时候需要知道第 n-1 个数和第 n-2 个数,也就是大问题被拆解成了小问题,这很符合递归的逻辑。

斐波那契数列是我们学过的第一个递归问题,很快我们就写出来下面的 python 代码:

class Solution:
def fib(self, n: int) -> int:
if n == 0:
return 0
if n == 1:
return 1
return (self.fib(n - 1) + self.fib(n - 2)) % 1000000007
  • 时间复杂度:

    O

    (

    2

    n

    )

    O(2 ^ n)

    O(2n),类似于二叉树的节点数。

  • 空间复杂度:

    O

    (

    n

    )

    O(n)

    O(n),栈的深度。

但是我们提交了之后,发现超时了。为什么呢?

因为,直接递归解法由于有很多的重复计算,比如计算 fib(3) 的时候需要计算 fib(2)fib(1);而计算 fib(4) 的时候,又计算了一遍 fib(3)

为了解决重复计算的问题,可以使用「记忆化搜索」,即可以使用字典保存计算过了的数字,省去了重复的计算,速度很快。

Python 代码如下:

# -*- coding:utf-8 -*-
class Solution:
def __init__(self):
self.keep = {0:0, 1:1} def Fibonacci(self, n):
if n in self.keep:
return self.keep[n]
else:
fn = self.Fibonacci(n - 1) + self.Fibonacci(n - 2)
self.keep[n] = fn
return fn

在 python3 中,可以使用语言自带的「记忆化递归」的注解:@functools.lru_cache(),它的用法只有一行,即在要执行记忆化的函数上面加上一行注解,即可在语言级别的自动化的记忆化递归。

class Solution:
@functools.lru_cache()
def fib(self, n: int) -> int:
if n == 0:
return 0
if n == 1:
return 1
return (self.fib(n - 1) + self.fib(n - 2)) % 1000000007
  • 时间复杂度:

    O

    (

    n

    )

    O(n)

    O(n),避免了重复的计算。

  • 空间复杂度:

    O

    (

    n

    )

    O(n)

    O(n),栈的深度。

动态规划

在「记忆化搜索」的基础上,再迈出一步,就能得到「动态规划」的解法。它们都是把大问题拆解成小问题的方法。

  • 记忆化搜索」:是 从顶向下 的思路,即把大问题一步步拆分成小问题;
  • 动态规划」:是 从底向上 的思路,即先得到小问题,然后再一步步推到出大问题。

动态规划的推导过程如下。

  • 定义状态:dp[i] 表示斐波拉契数列的第 n 个数字;
  • 状态转移方程:F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
  • 初始条件: F(0) = 0, F(1) = 1

Python 的代码如下:

class Solution(object):
def fib(self, n):
"""
:type n: int
:rtype: int
"""
if n == 0:
return 0
if n == 1:
return 1
dp = [0] * (n + 1)
dp[1] = 1
for i in range(2, n + 1):
dp[i] = (dp[i - 1] + dp[i - 2]) % 1000000007
return dp[n]

C++ 代码如下:

class Solution {
public:
int fib(int n) {
vector<int> dp(101, -1);
dp[0] = 0;
dp[1] = 1;
for (int i = 2; i <= n; ++i) {
dp[i] = (dp[i - 1] + dp[i - 2]) % 1000000007;
}
return dp[n];
}
};
  • 时间复杂度:

    O

    (

    n

    )

    O(n)

    O(n)

  • 空间复杂度:

    O

    (

    n

    )

    O(n)

    O(n)

由于 dp[i] 的状态只跟 dp[i - 1]dp[i - 2] 有关,所以可以进行状态的压缩,从而降低空间复杂度。即使用 3 个变量,分别表示

python 代码如下:

class Solution:
def fib(self, n: int) -> int:
a, b = 0, 1
for i in range(n):
a, b = b, (a + b) % 1000000007
return a

日期

2018 年 3 月 9 日
2021 年 8 月 5 日 —— 昨天去拍婚纱照了

【剑指Offer】10- I. 斐波那契数列 解题报告(Python & C++)的更多相关文章

  1. 剑指offer七之斐波那契数列

    一.题目 大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项.n<=39. 二.思路 序号:                  0  1   2   3  4   5  ...

  2. 剑指offer 07:斐波那契数列

    题目描述 大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0).(n<=39) 法一: public class Solution { publi ...

  3. 【剑指 Offer】10-I.斐波那契数列

    题目描述 写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项.斐波那契数列的定义如下: F(0) = 0,   F(1) = 1 F(N) = F(N - 1) + F(N - ...

  4. 剑指offer-面试题9.斐波拉契数列

    题目一:写一个函数,输入n,求斐波拉契数列的第n项. 斐波拉契数列的定义如下: { n=; f(n)={ n=; { f(n-)+f(n-) n>; 斐波拉契问题很明显我们会想到用递归来解决: ...

  5. 剑指offer-矩形覆盖-斐波那契数列(递归,递推)

    class Solution { public: int rectCover(int number) { if(number==0 || number==1||number==2) return nu ...

  6. hdu4549_M斐波那契数列 解题报告

    Solution: 1.快速幂:数/矩阵 2.以证明1000000007是素数. 费马小定理: 若p是素数,gcd(a,p)=1,则a^(p-1)1(mod p). 若a^b mod p 中b很大,则 ...

  7. 剑指offer——面试题10:斐波那契数列

    个人答案: #include"iostream" #include"stdio.h" #include"string.h" using na ...

  8. 【剑指Offer】合并两个排序的链表 解题报告(Python)

    [剑指Offer]合并两个排序的链表 解题报告(Python) 标签(空格分隔): LeetCode 题目地址:https://www.nowcoder.com/ta/coding-interview ...

  9. 【剑指Offer】旋转数组中的最小数字 解题报告(Python)

    [剑指Offer]旋转数组中的最小数字 解题报告(Python) 标签(空格分隔): LeetCode 题目地址:https://www.nowcoder.com/ta/coding-intervie ...

随机推荐

  1. 从零开始学习oracle

    引用博客:https://blog.csdn.net/qq_36998053/article/details/82725765 )Oracle之<环境配置> (二)Oracle之<基 ...

  2. Netty | 第1章 Java NIO 网络编程《Netty In Action》

    目录 前言 1. Java 网络编程 1.1 Javs NIO 基本介绍 1.2 缓冲区 Buffer 1.2 通道 Channel 1.3 选择器 Selector 1.4 NIO 非阻塞网络编程原 ...

  3. 一个专业处理字符串的IDEA插件

    字符串处理想必是小伙伴们平时开发时经常碰到的一个 "难题".为什么要打上引号?因为你说他难吧,其实也不是什么特别复杂的事:你说他不难吧,弄起来还真挺麻烦的,像删除其中空行啊.切换大 ...

  4. 淘宝、网易移动端 px 转换 rem 原理,Vue-cli 实现 px 转换 rem

       在过去的一段时间里面一直在使用Vue配合 lib-flexible和px2rem-loader配合做移动端的网页适配.秉着求知的思想,今天决定对他的原理进行分析.目前网上比较主流使用的就是淘宝方 ...

  5. 在 windows 系统上 安装与配置 PHP + Apache

    参考:http://www.cnblogs.com/pharen/archive/2012/02/06/2340628.html 在大学时候上过一门PHP课时,因为课堂需要配置过一次PHP+Mysql ...

  6. IPv6 私有地址

    在互联网的地址架构中,专用网络是指遵守RFC 1918(IPV4)和RFC 4193(IPV6)规范,使用专用IP地址空间的网络.私有IP无法直接连接互联网,需要使用网络地址转换(Network Ad ...

  7. Excel 数据验证:分类选择及输入限制

    几个简单设置让你的数据不再出错 如何快速选择某一大类中的细分小类 多级菜单 注意:引用可以创建二级目录,但是引用前应先用公式定义名称,然后引用,引用只能在本sheet操作.

  8. oracle中注释都是问号?中文显示不出来问题

    本人在工作中需要把开发上的库恢复到自己的虚拟机里面,然而捣鼓了许久建立好数据库之后,在使用建表语句初始化表的时候,发现注释都是????? 然后一脸懵逼不知何解,网上一大堆是说修改环境变量 NLS_LA ...

  9. redis入门到精通系列(四):Jedis--使用java操作redis详解

    (一)前言 如果不把数据库和后端语言联系起来,就起不到数据库应该要起到的作用.Java语言通过JDBC操作mysql,用Jedis操作redis.当然了,java操作redis的方式不止jedis一种 ...

  10. VUE页面实现加载外部HTML方法

    前后端分离,后端提供了接口.但有一部分数据,比较产品说明文件,是存在其他的服务器上的.所以,在页面显示的时候,如果以页面内嵌的形式显示这个说明文件.需要搞点事情以达到想要的效果.本文主要和大家介绍VU ...