描述
大家都知道斐波那契数列,现在要求输入一个正整数 n ,请你输出斐波那契数列的第 n 项。
斐波那契数列是一个满足
的数列。数据范围: 1 ≤ n ≤ 40
要求:空间复杂度 O(1),时间复杂度 O(n) ,本题也有时间复杂度 O(logn) 的解法
输入描述:
一个正整数n
返回值描述:
输出一个正整数。
示例1
输入:4
返回值:3
说明:根据斐波那契数列的定义可知,fib(1)=1,fib(2)=1,fib(3)=fib(3-1)+fib(3-2)=2,fib(4)=fib(4-1)+fib(4-2)=3,所以答案为3。
示例2
输入:1
返回值:1
示例3
输入:2
返回值:1
方法一:递归
斐波那契数列, 经典题. 它的递归定义式是 f(n) = f(n-1) + f(n-2)
, 因此通过递归来实现.
class Solution {
public:
int Fibonacci(int n) {
if(n==0) return 0;
if(n==1) return 1;
return Fibonacci(n-1) + Fibonacci(n-2);
}
};
运行时间:343ms
超过12.03% 用C++提交的代码
占用内存:652KB
超过38.59%用C++提交的代码
上面的递归轨迹我们可以自己心里推导一下, 发现会有许多重复的计算. 比方说计算 f(5)
时根据f(5)=f(4)+f(3)
计算了一遍 f(3)
, 后续计算 f(4)
时又根据 f(4)=f(3)+f(2)
对 f(3)
重复计算了一次. 可以画个递归树便于形象地理解.
方法二:动态规划
虽然方法一可以解决此题了,但是如果想让空间继续优化,那就用动态规划,优化掉递归栈空间。 方法二是从上往下递归的然后再从下往上回溯的,最后回溯的时候来合并子树从而求得答案。 那么动态规划不同的是,不用递归的过程,直接从子树求得答案。过程是从下往上
class Solution {
public:
int dp[50]{0};
int Fibonacci(int n) {
dp[1] = 1, dp[2] =1;
for (int i = 3 ; i <= n ; i ++) dp[i] = dp[i-1]+dp[i-2];
return dp[n];
}
};
继续优化 发现计算 f[5] 的时候只用到了 f[4]和f[3], 没有用到f[2]…f[0],所以保存f[2]…f[0]是浪费了空间。 只需要用3个变量即可。
class Solution {
public:
int Fibonacci(int n) {
int a = 1 , b = 1 , c = 1;
for (int i = 3 ; i <= n ; i ++) {
c = a + b , a = b , b = c;
}
return c;
}
};
运行时间:3ms
超过42.75% 用C++提交的代码
占用内存:528KB
超过59.68%用C++提交的代码