一、题目
斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:
F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1
给定 n ,请计算 F(n) 。
示例 1:
输入:n = 2
输出:1
解释:F(2) = F(1) + F(0) = 1 + 0 = 1
示例 2:
输入:n = 3
输出:2
解释:F(3) = F(2) + F(1) = 1 + 1 = 2
示例 3:
输入:n = 4
输出:3
解释:F(4) = F(3) + F(2) = 2 + 1 = 3
提示:
0 <= n <= 30
来源:力扣(LeetCode)
链接: https://leetcode.cn/problems/fibonacci-number
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
二、代码
1、解法1:直接递归
int sumNums(int n){
char a[n][n+1];
return sizeof(a) >> 1;//使用位运算来避免乘除号
}
2、解法2:递归优化(备忘录)
/*
在计算f(n) = f(n - 1) + f(n - 2)时候,我们会发现有很多是重复计算的,增加了时间复杂度,比如:f(5) = f(4) + f(3)
= f(3) + f(2) + f(2) + f(1)
= f(2) + f(1) + f(1) + f(0) + f(1) + f(0) + 1
= f(1) + f(0) + 1 + 1 + 0 + 1 + 0 + 1=.....
此时,f(3)计算了两次,f(2)计算了3次。
如果我们把这些重复的计算省去,可以大大减少时间复杂度。
那么如何省去这些重复的计算呢?
可以把已经计算的结果保存下来,等到下次需要用到这个结果时候直接使用,类似“备忘录”。
*/
int fib(int n){
int rem[31];//直接使用数据来存取计算的结果,由于题目说明n<=30所以数组大小直接设置31
//先初始化数组
for(int i = 0; i < 31; i++){
rem[i] = -1;
}
//设置两个初始值
rem[0] = 0;
rem[1] = 1;
return helper(rem,n);
}
int helper(int* arr, int n){
if(arr[n] == -1){//当数组中对应的值为-1说明该结果未曾计算过,需要计算
arr[n] = helper(arr, n - 1) + helper(arr , n - 2);//计算的结果保存到数组中
}
return arr[n];//数组对应的值不为-1说明该值已有,直接使用。
}
解法3:非递归
/*
如果把费波纳数列
0 1 1 2 3 5 8 13 21 34 55 .....
数值小的一端看作是前,数值大的一端看作是后,那么,我们思考f(n) = f(n - 1) + f(n - 2)这个公式时,是一种从后向前的推导思路,可以直观的感受到该问题是把大问题变为小问题,我们很容易会联想到递归的方法。但是,如果我们从前向后思路来看待这个问题,计算f(5),我们从前向后计算f(2),f(3),f(4);计算f(6),我们从前向后计算f(2),f(3),f(4),f(5)。这,是否可以用循环来实现呢?
*/
int fib(int n){
int arr[31];
arr[0] = 0;
arr[1] = 1;
for(int i = 2 ; i <= n; i++){
arr[i] = arr[i - 1] + arr[i - 2];//每一项都是前两项之和,从前向后计算,计算f(n)只要不停的从前面开始计算迭代到f(n)即可
}
return arr[n];
}
解法4:解法3的优化
/*
解法3中,空间复杂度为O(n),但在计算f(n)只需要f(n - 1),f(n - 2)这两项,那么是否可以用三个变量表示这三个数呢?如果可以,空间复杂度直接变为O(1)。
*/
int fib(int n){
if(n == 0) return 0;
if(n == 1) return 1;
int arr0 = 0;//第一项
int arr1 = 1;//第二项
int arr2 = 1;//第三项
for(int i = 2 ; i <= n; i++){
arr2 = arr0 + arr1;//第三项 = 第一项 + 第二项
arr0 = arr1;//一、二、三项,从右向左依次次顺位,第三项是下次计算结果
arr1 = arr2;
}
return arr2;
}
三、总结
在进行递归编程的时候,经常会遇到时间、空间复杂度十分大的算法,那么可以适当考虑时空复杂度大的原因是什么?是否可以降低时空复杂度?尝试着去降低时空复杂度。本题在递归解法1中时间复杂度大,通过画递归树可以明白其原因是因为有大量重复的计算,因此,从这个切入点考虑是否可以减少重复的计算,来减少时间复杂度。