509. Fibonacci Number(斐波那契数列)————附带详细解析和代码
原创
©著作权归作者所有:来自51CTO博客作者fanxinglanyu的原创作品,请联系作者获取转载授权,否则将追究法律责任
文章目录
0 效果
1 题目
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) 当前还没有被计算过。
然后就可以在递归中判断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)。
斐波那契数列递归图:
斐波那契数列记忆化搜索示意图:
如计算F(45),直接使用递归用时9.082809,用记忆化搜索仅仅0.000001
2.2 矩阵快速幂
以下解析摘自官网
2.3 通项公式
以下解析摘自官网
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);
}
};