文章目录


0 效果

509. Fibonacci Number(斐波那契数列)————附带详细解析和代码_算法

1 题目

509. Fibonacci Number(斐波那契数列)————附带详细解析和代码_矩阵快速幂_02

2 思路

2.1 动态规划(打表)

递归写法为:

int F(int n){
if(n == 0 || n == 1) return 1;
else return F(n-1) + F(n-2);
}

递归会涉及到很多重复的计算,如当n==5时,可以得到F(5)= F(4)+F(3),接下来计算F(4)时又会有F(4)= F(3)+F(2),这时不采取措施,F(3)将会被计算两次。如果n很大,重复计算的次数将难以想象。

实际上由于没有保存中间计算的结果,实际复杂度将会高达O(2n),即每次都会计算F(n-1)和F(n-2)这两个分支,基本上不能承受n较大的情况。

开一个数组dp,用来保存已经计算过的结果,其中dp[n]记录F(n)的结果,并用dp[n] = -1来表示F(n) 当前还没有被计算过。

int dp[MAXN];

然后就可以在递归中判断dp[n]是否是-1,如果不是-1,说明已经计算过F(n),直接返回dp[n]就是结果;否则,按照递归式进行递归。

int F(int n){
if(n == 0 || n == 1) return 1;
if(dp[n] != -1) return dp[n];
else{
dp[n] = F(n-1) + F(n-2);
return dp[n];
}
}

通过记忆化搜素,把复杂度从O(2n)降到O(n)。
斐波那契数列递归图:
509. Fibonacci Number(斐波那契数列)————附带详细解析和代码_算法_03
斐波那契数列记忆化搜索示意图:
509. Fibonacci Number(斐波那契数列)————附带详细解析和代码_动态规划_04

如计算F(45),直接使用递归用时9.082809,用记忆化搜索仅仅0.000001

2.2 矩阵快速幂

以下解析摘自​​官网​

509. Fibonacci Number(斐波那契数列)————附带详细解析和代码_打表_05

2.3 通项公式

以下解析摘自​​官网​

509. Fibonacci Number(斐波那契数列)————附带详细解析和代码_算法_06

3 代码

3.1 打表

class Solution {
public:
int fib(int n) {
int dp[31];
dp[0] = 0;
dp[1] = 1;
for(int i = 2;i <= n;i++){
dp[i] = dp[i -1] + dp[i-2];
}
return dp[n];
}
};

3.2 矩阵快速乘

class Solution {
public:
int fib(int n) {
if (n < 2) {
return n;
}
vector<vector<int>> q{{1, 1}, {1, 0}};
vector<vector<int>> res = matrix_pow(q, n - 1);
return res[0][0];
}

vector<vector<int>> matrix_pow(vector<vector<int>>& a, int n) {
vector<vector<int>> ret{{1, 0}, {0, 1}};
while (n > 0) {
if (n & 1) {
ret = matrix_multiply(ret, a);
}
n >>= 1;
a = matrix_multiply(a, a);
}
return ret;
}

vector<vector<int>> matrix_multiply(vector<vector<int>>& a, vector<vector<int>>& b) {
vector<vector<int>> c{{0, 0}, {0, 0}};
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
c[i][j] = a[i][0] * b[0][j] + a[i][1] * b[1][j];
}
}
return c;
}
};

3.3 通项公式

class Solution {
public:
int fib(int n) {
double sqrt5 = sqrt(5);
double fibN = pow((1 + sqrt5) / 2, n) - pow((1 - sqrt5) / 2, n);
return round(fibN / sqrt5);
}
};