题目描述
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。n<=39
下面首先会简单介绍递归算法,然后给出递归算法的解法代码,以及使用非递归算法的解决代码,最后是简单使用动态规划的方法解决。
递归算法
斐波那契数列是典型的递归代表。递归算法的核心在于两点:
- 结束递归的边界条件
- 递归公式
递归公式简单理解起来,就是类似于我们高中数学里可以用式子表达的数列,类似于初中学的找前后的规律,只是把这个规律公式化了。
递归算法的两个核心要点,对应到代码算法的实现,也就是以下 3 步:
- 找整个递归的终止条件:递归应该在什么时候结束?
- 找返回值:应该给上一级返回什么信息?
- 本级递归应该做什么:这一级递归中,要完成什么任务?
举个例子:假设有个数列 1 3 5 7 9 11 … 找到第n项的值。在这个数列中,开始的一项是 1,是无规律可循的一项,这一项到数列的尽头了,是 “结束” 项;此后,每一项都是在其前一项的基础上加 2,得到的。因此可以写为:
- 边界条件:f(1) = 1
- 递归公式:f(n) = f(n-1)+2
找到上述的两个要点,就可以实现递归算法。
解题思路
这一道题中,要求返回斐波那契数列的第 n 项,从 0 开始。
斐波那契数列:0 1 1 2 3 5 8 13 …
可以知道其边界条件和递归公式分别为:
- fibonacci(1) = 0, fibonacci(1) = 1,fibonacci(2) = 1;
- fibonacci(n) = fibonacci(n-1)+fibonacci(n-2)
实现代码:
class Solution {
public:
int Fibonacci(int n) {
if(n==0){
return 0;
}
else if(n==1 || n==2){
return 1;
}
else{
return Fibonacci(n-1)+Fibonacci(n-2);
}
}
};
其实,递归算法的效率不一定很高,而且消耗的存储空间也不少,但是用递归算法有时候实现代码会更简洁。上述算法的时间复杂度为 。
当然,这道题不用递归也可以解决。我们在算数列中的某一项时,也不会采用递归这种方式,而是会从第一项开始算起,由于斐波那契数列的递归公式计算 F(n) 时,需要用到 F(n-1) 和 F(n-2),所以利用中间变量记录这两项就可以了。
实现代码:
class Solution {
public:
int Fibonacci(int n) {
if(n==0 || n==1){
return n;
}
int a=0,b=1; // a代表F(n-2), b代表F(n-1)
int res = 0;
for(int i=2;i<=n;i++){
res = a + b;
a = b;
b = res;
}
return res;
}
};
这个方法空间复杂度只需要 ,时间复杂度为 。
在牛客网的讨论区里还有人使用了动态规划解决这道题。
什么是动态规划?
动态规划方法一般用来求解最优化问题。动态规划应用于子问题重叠的情况,即不同的子问题具有公共的子子问题(子问题的求解是递归进行的,将其划分为更小的子子问题)。
动态规划包含哪些步骤?
解决动态规划问题一般分为四步:
- 定义一个状态,这是一个最优解的结构特征
- 进行状态递推,得到递推公式
- 进行初始化
- 返回结果
按照上述步骤,分析这一题:
- 状态:f(i)表示第i个斐波那契数
- 初始化:f(0)=0, f(1)=1, f(2)=1
- 状态递推:f(i) = f(i-1)+f(i-2)
- 返回结果 f(n)
实现起来其实和上面的非递归算法差不多,为了更加简洁,使用 f 代表 f(i-2),使用 g 代表 f(i-1),状态递推等于 g+f,而 g+f 会成为下一次计算的 f(i-1),也就是 g,所以再次赋给 g,g’=g+f;而 f 应该更新为原来的 g,也等于g’-f,所以有 f = g-f。
实现代码:
class Solution {
public:
int Fibonacci(int n) {
if(n<=0) return 0;
int f = 0, g = 1;
while(n--) {
g += f;
f = g - f;
}
return f;
}
};
参考:
来源:CSDN
作者:名字要够长系列
链接:https://blog.csdn.net/qq_36641919/article/details/104167732