LeetCode 0509. 斐波那契数:尝试以四种方式吃透(四种大方法+两种小优化)
Posted Tisfy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 0509. 斐波那契数:尝试以四种方式吃透(四种大方法+两种小优化)相关的知识,希望对你有一定的参考价值。
【LetMeFly】尝试以四种方式吃透:509.斐波那契数(四种大方法+两种小优化)
先说明本题解法:
- 动态规划(及 原地滚动的优化)
- 递归(及 记忆化的优化)
- 矩阵快速幂
- 通项公式
力扣题目链接:https://leetcode.cn/problems/fibonacci-number/
斐波那契数 (通常用 F(n)
表示)形成的序列称为 斐波那契数列 。该数列由 0
和 1
开始,后面的每一项数字都是前面两项数字的和。也就是:
F(0) = 0,F(1) = 1 F(n) = F(n - 1) + F(n - 2),其中 n > 1
给定 n
,请计算 F(n)
。
示例 1:
输入:n = 2 输出:1 解释:F(2) = F(1) + F(0) = 1 + 0 = 1
示例 2:
输入:n = 3 输出:2 解释:F(3) = F(2) + F(1) = 1 + 1 = 2
示例 3:
输入:n = 4 输出:3 解释:F(4) = F(3) + F(2) = 2 + 1 = 3
提示:
0 <= n <= 30
方法一:动态规划
开辟一个大小为 30 30 30的数组 a a a(或开辟大小为 n + 1 n+1 n+1的数组也可)
初始值 a [ 0 ] = 0 , a [ 1 ] = 1 a[0] = 0, a[1] = 1 a[0]=0,a[1]=1
之后从下标 2 2 2开始到 n n n为止,使用转移方程 a [ n ] = a [ n − 1 ] + a [ n − 2 ] a[n] = a[n - 1] + a[n - 2] a[n]=a[n−1]+a[n−2]求解
最终返回a[n]
即可
- 时间复杂度 O ( n ) O(n) O(n)
- 空间复杂度 O ( C ) O(C) O(C),这里 C C C是数据中 n n n的最大值 30 30 30(也可以只开辟 n + 1 n+1 n+1的数组,则空间复杂度为 O ( n ) O(n) O(n))
AC代码
C++
class Solution // 开辟一整个数组
public:
int fib(int n)
int a[31] = 0, 1;
for (int i = 2; i <= n; i++)
a[i] = a[i - 1] + a[i - 2];
return a[n];
;
方法一.2:动态规划 + 原地滚动
不难发现,在计算 a [ n ] a[n] a[n]时,我们只用到了 a [ n − 1 ] a[n-1] a[n−1]和 a [ n − 2 ] a[n-2] a[n−2],再往前的数据就再也用不到了
因此,我们只需要使用两个额外的空间 0 _0 0和 1 _1 1来分别记录 a [ n − 1 ] a[n-1] a[n−1]和a[n-2] 的值,在计算过程中,不断更新 的值,在计算过程中,不断更新 的值,在计算过程中,不断更新_0 和 和 和_1$的值即可
- 时间复杂度 O ( n ) O(n) O(n)
- 空间复杂度 O ( 1 ) O(1) O(1)
AC代码
C++
class Solution // 两个额外变量模拟
public:
int fib(int n)
if (n < 2)
return n;
int _0 = 0, _1 = 1; // 分别代表a[n - 2]和a[n - 1]
int ans;
for (int i = 2; i <= n; i++)
ans = _0 + _1;
_0 = _1, _1 = ans; // Update
return ans;
;
方法二:递归
斐波那契数列很容易看出“递归”
题目都说明了, F ( n ) = F ( n − 1 ) + F ( n − 2 ) F(n) = F(n - 1) + F(n - 2) F(n)=F(n−1)+F(n−2),终止条件是 n = 0 n = 0 n=0或 n = 1 n = 1 n=1
那么,我们只需要在非终止条件下无脑递归即可。
- 时间复杂度 O ( n 2 ) O(n^2) O(n2),这个时间复杂度待会儿分析
- 空间复杂度 O ( n ) O(n) O(n),不论总递归次数为多少,总递归深度为 n n n
AC代码
C++
class Solution // 递归
public:
int fib(int n)
if (n < 2)
return n;
return fib(n - 1) + fib(n - 2);
;
方法二.2:递归 + 记忆化
方法二在数据量小的前提下能很轻松地计算出结果。
但是为什么方法二的时间复杂度是 O ( n 2 ) O(n^2) O(n2)呢?
不难发现,计算 F ( 5 ) F(5) F(5)时,会调用 F ( 4 ) F(4) F(4)和 F ( 3 ) F(3) F(3),但在计算 F ( 4 ) F(4) F(4)时,会再调用一次 F ( 3 ) F(3) F(3),也就是说 F ( 3 ) F(3) F(3)不只被调用了一次
例如计算 F ( 6 ) F(6) F(6)时:
F ( 4 ) F(4) F(4)计算了两次, F ( 3 ) F(3) F(3)计算了三次, F ( 2 ) F(2) F(2)更是计算了五次。
n n n越大,这种重复计算就越明显。
那么,既然算过一遍 F ( 3 ) F(3) F(3)了,为什么要再算一次呢?
记忆化出现了
我们使用一个哈希表,将计算过的结果记录下来,那么再次调用这个函数时,直接返回之前计算过的结果不就可以了吗?
这样就避免了没必要的重复计算。
- 时间复杂度 O ( n ) O(n) O(n)
- 空间复杂度 O ( n ) O(n) O(n)
AC代码
C++
class Solution // 递归 + 记忆化
private:
unordered_map<int, int> ma;
public:
int fib(int n)
if (n < 2)
return n;
if (ma.count(n))
return ma[n];
return ma[n] = fib(n - 1) + fib(n - 2);
;
方法三:矩阵快速幂
[ 1 1 1 0 ] [ a n a n − 1 ] = [ a n + a n − 1 a n ] = [ a n + 1 a n ] \\left[\\beginarrayll 1 & 1 \\\\ 1 & 0 \\endarray\\right]\\left[\\beginarrayc a_n \\\\ a_n-1 \\endarray\\right]=\\left[\\beginarrayc a_n+a_n-1 \\\\ a_n \\endarray\\right]=\\left[\\beginarrayc a_n+1 \\\\ a_n \\endarray\\right] [1110][anan−1]=[an+an−1an]=[an+1an]
因此
[ a n + 1 a n ] = [ 1 1 1 0 ] n [ a 1 a 0 ] \\left[\\beginarrayc a_n+1 \\\\ a_n \\endarray\\right]=\\left[\\beginarrayll 1 & 1 \\\\ 1 & 0 \\endarray\\right]^n\\left[\\beginarrayl a_1 \\\\ a_0 \\endarray\\right] [an+1an]=[1110]n[a1a0]
因此可以使用矩阵快速幂求出
[
1
1
以上是关于LeetCode 0509. 斐波那契数:尝试以四种方式吃透(四种大方法+两种小优化)的主要内容,如果未能解决你的问题,请参考以下文章