509.斐波那契数
题目
斐波那契数,通常用 F(n) 表示,形成的序列称为斐波那契数列。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
给定 N,计算 F(N)。
示例1:
输入:2
输出:1
解释:F(2) = F(1) + F(0) = 1 + 0 = 1.
示例2:
输入:3
输出:2
解释:F(3) = F(2) + F(1) = 1 + 1 = 2.
提示:
0 ≤ N ≤ 30
解题思路
- 由给出的推导式F(N) = F(N - 1) + F(N - 2)和初始值F(0) = 0, F(1) = 1,看出适合用递归的方法。
- 函数的基线条件为当N=0和N=1时的初始值;
- 函数的递归条件则为推导式。
代码提交
class Solution
def fib(self, n: int) -> int:
if n == 0:
return 0
elif n == 1:
return 1
else:
return self.fib(n-1) + self.fib(n-2)
执行用时 :808 ms, 在所有 Python3 提交中击败了20.73%的用户
内存消耗 :12.6 MB, 在所有 Python3 提交中击败了50.00%的用户
面试题10- I. 斐波那契数列
题目
写一个函数,输入 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。
示例1:
输入:n = 2
输出:1
示例2:
输入:n = 5
输出:5
提示:
0 <= n <= 100
解题思路
当使用上面递归的函数来解决这题,点击“提交”之后,就会发生超出时间限制的错误,重新看一遍题目会发现,两题的不同点在于提示,上一题的N值小于等于30,而这一题的N值小于等于100,说明这题需要的计算量是更大的,所以递归函数花费的时间更多。
- 新建nums字典,用于保存n及其计算过的斐波拉契数列的数值,在字典中为对应为键值对;
- 先把n=0和n=1的值添加入字典;
- 当n大于1时,进入一个循环,循环区间为[2, n+1),左闭右开,所以右区间为n+1才能包括循环到n;
- 每计算一个值,就把i和对应的值加入到字典中;
- 最后把结果强制为整形返回。
代码提交
def fibonacci(n):
nums = {}
nums[0] = 0
nums[1] = 1
if n > 1:
for i in range(2,n+1):
nums[i] = nums[i-2] + nums[i-1]
return int(nums[n]%(1000000007))
执行用时 :28 ms, 在所有 Python3 提交中击败了97.37%的用户
内存消耗 :13.6 MB, 在所有 Python3 提交中击败了100.00%的用户
拓展
1.两题比较分析
- 递归用在第二题会发生超出时间限制的错误,为什么递归所花费的时间这么多呢,下面我们看看上题的递归是怎么实现的,如算F(5):
- 从上图的递归过程可以看出,因为有F(2) 进行了重复计算,如果n比较大的话,那么就会有很多的数进行重复的递归,这就直接导致函数所需的时间比较多。因此当数比较小时可以考虑递归函数。
- 而第二题用到的方法是把已经计算过的数用键值对的方式保存在字典中,等需要用到的时候直接调用即可,不需重复计算,所以大大减少了计算所需的时间。
2.其他
- 第一个代码,因为递归时是调用类的方法,类中的self会替换成类的实例,所以调用写成
self.fib()
。 - 第二个代码,为什么使用字典而不用列表?
因为字典可以直接对添加键并赋值,列表需要用append()函数添加元素,不然会出现超出索引的错误。