LeetCode第509题斐波那契数

一、题目

斐波那契数 (通常用 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说明该值已有,直接使用。
}
  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];
}
  1. 解法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中时间复杂度大,通过画递归树可以明白其原因是因为有大量重复的计算,因此,从这个切入点考虑是否可以减少重复的计算,来减少时间复杂度。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值