LeetCode剑指offer 10-I 斐波那契数列(c++)


题目要求

写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项。斐波那契数列的定义如下:

F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/fei-bo-na-qi-shu-lie-lcof


下面展示的解法代表了我对题目的处理思路——首先是递归,算是拿到题目第一瞬间就想到的做法,在此基础上,参考了其他人的见解,整理出动态规划的做法。


一、基础解法——递归

斐波那契数列算是剑指上面对递归应用的入门级题目,思路其实就是套娃:你想要求解数列的第n项,就要先求解第n项的前面两项,其中前面两项的求法和第n项的求法一样——即需要知道对应项的前面两项,即公式
F(N) = F(N - 1) + F(N - 2)
这么求下去岂不是永无止尽(jinzhitaowa),所以我们才需要另一个条件——终止条件,即第一项和第二项的值已知,当n = 0时,F(0) = 0;n = 1时,F(1) = 1。
转化成代码:

int fib(int n) {
    if (n == 0 || n == 1) {
        return n;
    }     
    return (fib(n-1) + fib(n-2))%1000000007;
}

在这里插入图片描述
很明显,超时,说明这并不是出题人想要的做法。其实简单想一下就知道为什么这种做法会有明显的缺陷:比如求解F(43),需要求F(42)和F(41),而求F(42)时,又需要求解一次F(41),是有额外的无用功的。当然我们也可以用一个二叉树来简单看一下重复计算的这些项,而后面的做法将会主要优化这一点。

二、标准解法——动态规划

前面的递归从二叉树的角度来看,是一种从根节点往某个叶子节点计算,而下面要展示的这种做法则是从底至顶的一种方法。即从索引低的项开始往高的项目算。

代码如下:

int fib(int n) {
    if (n == 0) {
        return 0;
    }
    vector<int> v(n+1,0);
    v[1] = 1;
    for (int i = 2; i <= n; ++i) {
        v[i] = (v[i-1] + v[i-2])%1000000007;
    }
    return v[n];
}

在这里插入图片描述

看来这道题已经被大家研究烂了,那我再想想怎么优化吧。

三、空间上的优化

到这里我的脑子已经不够用了,时间复杂度上已经是O(n)(相当于一个n+1大小的数据遍历),所以我已经没有太好的思路去做这方面的优化了。唯一我能想到的就是上面那种做法其实借助了辅助数组,那么我把数组拿掉,应该能从空间上做一定程度的优化。

其实也很简单,就是常用做法——反复横跳。只建三个临时变量,然后相互赋值,直到找到n所在的那个位置(那一组三个数的位置)停止。

int fib(int n) {
    if (n == 0) {
        return 0;
    }
    int temp[3] = {0, 1, 1};
    for (int i = 1; i < n/3 + 1; ++i) {
        temp[0] = (temp[1] + temp[2])%1000000007;
        temp[1] = (temp[2] + temp[0])%1000000007;
        temp[2] = (temp[0] + temp[1])%1000000007;
    }
    return temp[n%3];
}

在这里插入图片描述
结果显示,不仅空间上得到了优化,时间上也得到了优化(当然不排除leetcode他本身判题带来的误差)。因为从我理解的来讲,本质上说,还是做了对n个项左右的遍历(虽然后面这一种做法是某种程度上可以说是O(n/3),但是本质上来讲,时间上应该和第二种做法差不多才对,如果有大手子明白的其中的差距,也希望能评论区指导一下)。


总结

虽然说是简单题,但是也还是有一些值得注意的点,如需要注意类型的范围,边界条件等等,虽然有点丢人,但还是贴出自己犯得错误。
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Claude的羽毛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值