1、题目描述
2、题目解析
斐波那契数列是典型中的典型题目!
(来自百度百科)。 斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardo Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……在数学上,斐波那契数列以如下被以递推的方法定义:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)。
解法一:递归
当时在读书的时候,这道题就是用来专门讲递归算法的题目,简单易懂逻辑。
因为存在以下的递推公式,所以递归代码非常容易
-
F(0) = 0,F(1) = 1 F(n) = F(n - 1) + F(n - 2),其中 n > 1
class Solution{
public int fib(int n) {
if(n==0) return 0;
if(n==1) return 1;
return fib(n-1) +fib(n-2);
}
}
复杂度分析
时间复杂度:O(n^2),递归方法效率是极其低下的,因为会涉及很多重复计算。
空间复杂度:O(n)。
【递归方法效率是极其低下】很多重复计算问题,所以编译后的结果如下,执行用时需要8ms, 仅击败了22.36%的用户:
解法二:动态规划
使用辅助空间预存每次计算的结果,这样后续每个数据需要获取数据的时候,直接拿之前已经计算好的结果即可,这样效率高很多。
class Solution{
public int fib(int n) {
if(n==0) return 0;
if(n==1) return 1;
//使用辅助空间,预存结果
int[] dp = new int[n+1];
dp[0] = 0;
dp[1] = 1;
for(int i = 2; i < n+1; i++){
dp[i] = dp[i-1] + dp[i-2];
}
return dp[n];
}
}
复杂度分析
时间复杂度:O(n),由于每一步骤都已经预存结果,因此时间复杂度就是O(n), for循环的。
空间复杂度:O(n),使用dp辅助空间,预存每次计算的结果。
此次编译效率就高很多,运行结果截图如下:
解法三:继续优化空间
在解决二的基础上,dp辅助空间可以不使用,优化成量模式,这样空间复杂度可以从O(n) 优化到O(1).
class Solution{
public int fib(int n) {
if(n==0) return 0;
if(n==1) return 1;
//使用变量模式,预存结果
int one = 0;
int two = 1;
int res = one + two;
for(int i = 2; i < n+1; i++){
res = one + two;
one = two;
two = res;
}
return res;
}
}
复杂度分析
时间复杂度:O(n),由于每一步骤都已经预存结果,因此时间复杂度就是O(n), for循环的。
空间复杂度:O(1),此时用变量模式,预存结果
综上面3种解法,动态规划中,dp[i] = dp[i - 1] + dp[i - 2](递推公式),其实是此类问题中比较难推导的,动态规划问题最困难的就是写出【状态转移方程】。只要能够推断出【状态转移方程】,优化方法无非是用备忘录或者 DP table,再无奥妙可言。