【剑指Offer】10- I. 斐波那契数列 解题报告(Python & C++)
- 作者: 负雪明烛
- 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++)的更多相关文章
- 剑指offer七之斐波那契数列
一.题目 大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项.n<=39. 二.思路 序号: 0 1 2 3 4 5 ...
- 剑指offer 07:斐波那契数列
题目描述 大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0).(n<=39) 法一: public class Solution { publi ...
- 【剑指 Offer】10-I.斐波那契数列
题目描述 写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项.斐波那契数列的定义如下: F(0) = 0, F(1) = 1 F(N) = F(N - 1) + F(N - ...
- 剑指offer-面试题9.斐波拉契数列
题目一:写一个函数,输入n,求斐波拉契数列的第n项. 斐波拉契数列的定义如下: { n=; f(n)={ n=; { f(n-)+f(n-) n>; 斐波拉契问题很明显我们会想到用递归来解决: ...
- 剑指offer-矩形覆盖-斐波那契数列(递归,递推)
class Solution { public: int rectCover(int number) { if(number==0 || number==1||number==2) return nu ...
- hdu4549_M斐波那契数列 解题报告
Solution: 1.快速幂:数/矩阵 2.以证明1000000007是素数. 费马小定理: 若p是素数,gcd(a,p)=1,则a^(p-1)1(mod p). 若a^b mod p 中b很大,则 ...
- 剑指offer——面试题10:斐波那契数列
个人答案: #include"iostream" #include"stdio.h" #include"string.h" using na ...
- 【剑指Offer】合并两个排序的链表 解题报告(Python)
[剑指Offer]合并两个排序的链表 解题报告(Python) 标签(空格分隔): LeetCode 题目地址:https://www.nowcoder.com/ta/coding-interview ...
- 【剑指Offer】旋转数组中的最小数字 解题报告(Python)
[剑指Offer]旋转数组中的最小数字 解题报告(Python) 标签(空格分隔): LeetCode 题目地址:https://www.nowcoder.com/ta/coding-intervie ...
随机推荐
- ubuntu20.04安装EasyConnect兼容性问题解决
目录 1. 命令行启动EasyConnect 2. 降级pango 3. 重新启动EasyConnect,即可成功启动 Ubuntu20.04安装EasyConnect后无法启动的解决方案 工作使用操 ...
- centos下Spin Version 6.3.2及ispin安装(2014.9.17)
centos下Spin Version 6.3.2及ispin安装(2014.9.17) 前言:windos下首先安装虚拟机,再安装linux系统(centos版) 一.本帖来源于官网http://s ...
- Freeswitch 安装爬坑记录1
2 Freeswitch的安装 2.1 准备工作 服务器安装CentOS 因为是内部环境,可以关闭一些防火墙设置,保证不会因为网络限制而不能连接 关闭防火墙 查看防火墙 systemctl statu ...
- java类加载、对象创建过程
类加载过程: 1, JVM会先去方法区中找有没有相应类的.class存在.如果有,就直接使用:如果没有,则把相关类的.class加载到方法区 2, 在.class加载到方法区时,会分为两部分加载:先加 ...
- 大数据学习day14-----第三阶段-----scala02------1. 元组 2.类、对象、继承、特质 3.函数(必须掌握)
1. 元组 映射是K/V对偶的集合,对偶是元组的最简单的形式,元组可以装着多个不同类型的值 1.1 特点 元组相当于一个特殊的数组,其长度和内容都可变,并且数组中可以装任何类型的数据,其主要用处就是存 ...
- AI作曲的一个点子
通常的AI作曲都是通过拆分音乐为几个声道, 然后再把各个声道拆成音符去分析. 我忽然之间有个想法,是否可以继续拆分下去. 音符就是一些有规则的高低电平,这样把音符拆成电平. 一定会带来巨大的运算,但如 ...
- 容器之分类与各种测试(四)——set
set和multiset的去别在于前者的key值不可以重复,所以用随机数作为其元素进行插入时,遇到重复元素就会被拒绝插入(但是程序不会崩溃). 例程 #include<stdexcept> ...
- 链栈(C++)
链栈,字面意思,就是用链表来实现一个栈的数据结构. 那么,只需将单链表的头节点当作栈顶,尾节点当作栈底.入栈只需要头插,出栈只需头删即可.所以只需要吧单链表稍微阉割一下就可以得到链式栈了.代码如下 / ...
- 转 序列化Serializable和Parcelable的区别详解
什么是序列化,为什么要进行序列化 答:对象要进行传输(如:activity 与activity间 ,网络间 进程间等等).存储到本地就必须进行序列化 . 这种可传输的状态就是序列化. 怎么序列化??两 ...
- Oracle中的instr函数
最近修改某个条件,由原来输入一个数据修改为可以输入多个,如图所示: 在实现时用到了regexp_substr函数进行分割连接起来的数据,查询时还用到了instr函数进行判断,但出现了问题,当子库存输入 ...