• 作者: 负雪明烛
  • 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. Docker环境中部署Prometheus及node-exporter监控主机资源

    前提条件 已部署docker 已部署grafana 需要开放 3000 9100 和 9090 端口 启动node-exporter docker run --name node-exporter - ...

  2. 一站式Flink&Spark平台解决方案——StreamX

    大家好,我是独孤风.今天为大家推荐的是一个完全开源的项目StreamX.该项目的发起者Ben也是我的好朋友. ****什么是StreamX,StreamX 是Flink & Spark极速开发 ...

  3. Angular Service设计理念及使用

    官方认为组件不应该直接获取或保存数据, 它们应该聚焦于展示数据,而把数据访问的职责委托给某个服务. 而服务就充当着数据访问,逻辑处理的功能.把组件和服务区分开,以提高模块性和复用性. 1.依赖注入 注 ...

  4. 链栈(C++)

    链栈,字面意思,就是用链表来实现一个栈的数据结构. 那么,只需将单链表的头节点当作栈顶,尾节点当作栈底.入栈只需要头插,出栈只需头删即可.所以只需要吧单链表稍微阉割一下就可以得到链式栈了.代码如下 / ...

  5. Oracle——生成Awr报告

    Oracle--生成Awr报告 AWR的概念 Oracle数据库是一个使用量很多的数据库,关于Oracle数据库的性能.Oracle10g以后,Oracle提供了一个性能检测的工具:AWR(Autom ...

  6. 节省内存的循环banner(一)

    循环banner是指scrollview首尾相连,循环播放的效果,使用非常广泛.例如淘宝的广告栏等. 如果是简单的做法可以把所有要显示的图片全部放进一个数组里,创建相同个数的图片视图来显示图片.这样的 ...

  7. NSURLSessionDownloadTask实现大文件下载

    - 4.1 涉及知识点(1)使用NSURLSession和NSURLSessionDownload可以很方便的实现文件下载操作 第一个参数:要下载文件的url路径 第二个参数:当接收完服务器返回的数据 ...

  8. 【Java 基础】Arrays.asList、ArrayList的subList注意事项

    1. 使用Arrays.asList的注意事项 1.1 可能会踩的坑 先来看下Arrays.asList的使用: List<Integer> statusList = Arrays.asL ...

  9. Spring Cloud Eureka源码分析之服务注册的流程与数据存储设计!

    Spring Cloud是一个生态,它提供了一套标准,这套标准可以通过不同的组件来实现,其中就包含服务注册/发现.熔断.负载均衡等,在spring-cloud-common这个包中,org.sprin ...

  10. 魅族CMDB运维自动化实践

    一.简介 原创:梁鹏 本文是根据魅族系统架构师梁鹏10月20日在msup携手魅族.Flyme.百度云主办的第十三期魅族技术开放日< 魅族CMDB运维自动化实践>演讲中的分享内容整理而成. ...