【面试题10-1 斐波那契数列】
难度: 简单
限制: 0 <= n <= 100
写一个函数,输入 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。
Leetcode题目对应位置: 面试题10-1:斐波那契数列
最直观的思路就是递归求解,将计算 f(n)
向下拆分为计算 f(n-1)
和 f(n-2)
,以 f(0)
和 f(1)
做为递归终止条件。但是递归法每次向下拆分计算时,总会重复计算一次 f(n-2)
,比较费时,所以递归实现效率通常不如循环。另外如果递归层级太多,容易出现调用栈溢出。
时间复杂度:O(2^n),类似于二叉树节点数
空间复杂度:O(n),递归调用需要空间消耗,每一次自身调用时,都需要在内存栈中分配空间以保存参数
直观但效率低下,面试如果这么做题那结果可能不太好。
递归法 Python 代码:
class Solution:
def fib(self, n: int) -> int:
return self.rec(n) % 1000000007
def rec(self, n):
if n == 0: return 0
if n == 1: return 1
return self.rec(n-1) + self.rec(n-2)
递归中有很多个小问题存在相互重叠的部分,比如本题中 f(n-2)
的计算。实际上如果从 f(0)
和 f(1)
开始计算,依次算出 f(2)
,f(3)
…,就能够算出 f(n)
,避免重复计算。
第一种: 直接建立一个列表,循环计算到 n 即可。
时间复杂度:O(n)
空间复杂度:O(n)
class Solution:
def fib(self, n: int) -> int:
f = [0, 1]
for i in range(2, n+1):
res = f[i-1] + f[i-2]
f.append(res)
return f[n] % 1000000007
第二种: 发现其实列表也没必要建,空间复杂度能够再优化。在循环体内将第 2 个计算数 fs
赋给第 1 个计算数 ff
,再将计算结果 fn
赋给第二个计算数 fs
,就能实现不断往前走,直到 n 计算停止。
时间复杂度:O(n)
空间复杂度:O(1)
class Solution:
def fib(self, n: int) -> int:
ff, fs = 0, 1
f = [0, 1]
if n < 2:
return f[n]
for i in range(2, n+1):
fn = ff + fs
ff = fs
fs = fn
return fn % 1000000007
介绍一个数学公式:
[ f ( n ) f ( n − 1 ) f ( n − 1 ) f ( n − 2 ) ] = [ 1 1 1 0 ] n − 1 \left[ \begin{matrix} f(n) & f(n-1) \\ f(n-1) & f(n-2) \end{matrix} \right] = \left[ \begin{matrix} 1 & 1 \\ 1 & 0 \end{matrix} \right] ^{n-1} [f(n)f(n−1)f(n−1)f(n−2)]=[1110]n−1
所以,要求 f(n)
只需要求得 [ 1 1 1 0 ] n − 1 \left[ \begin{matrix} 1 & 1 \\ 1 & 0 \end{matrix} \right] ^{n-1} [1110]n−1 即可,而乘方又有以下性质:
a n = { a n / 2 ∗ a n / 2 n 为 偶 数 a ( n − 1 ) / 2 ∗ a ( n − 1 ) / 2 ∗ a n 为 奇 数 a^n = \begin{cases} a^{n/2} * a^{n/2} & n 为偶数 \\ a^{(n-1)/2} * a^{(n-1)/2} * a & n 为奇数 \end{cases} an={an/2∗an/2a(n−1)/2∗a(n−1)/2∗an为偶数n为奇数
所以,要求得 n 次方,只要先求得 n/2 次方,再把结果平方一下就可以了。
时间复杂度:O(logn)
空间复杂度:O(1)
class Solution:
def fib(self, n: int) -> int:
f = [0, 1]
if n < 2: return f[n]
res = self.matrixPower(n - 1)
return res[0][0] % 1000000007
def matrixPower(self, n):
m = [[1, 1], [1, 0]]
if n == 1:
mat = m
elif n % 2 == 0:
mat = self.matrixPower(n / 2)
mat = self.matrixMultiply(mat, mat)
else:
mat = self.matrixPower((n-1) / 2)
mat = self.matrixMultiply(mat, mat)
mat = self.matrixMultiply(mat, m)
return mat
def matrixMultiply(self, m1, m2):
return [[ m1[0][0] * m2[0][0] + m1[0][1] * m2[1][0],
m1[0][0] * m2[0][1] + m1[0][1] * m2[1][1] ],
[ m1[1][0] * m2[0][0] + m1[1][1] * m2[1][0],
m1[1][0] * m2[0][1] + m1[1][1] * m2[1][1] ]]
实现这种解法代码比较复杂,不太适合面试,所以不算很实用。
参考资料:
[1] 剑指 offer 第二版
[2] LeetCode 面试题10:斐波那契数列