【攻克剑指offer】第3天《斐波那契数列》


题目描述: 💦
在这里插入图片描述
👉题目链接👈

🏠解法一、暴力递归

看题目我们首先来了解2个问题:

1e9+7为什么等于1000000007?

什么是1e9
通常来说这是计算机中一种科学计数法的表示形式:
1e9 = 1*10^9 = 1000000000;
例如:9e8 = 9*10^8 = 900000000;
e表示10,e后面的数字表示次方,e的多少次方。

为什么要取模?

  大数越界:随着 n 增大, f(n)会超过 Int32 甚至 Int64 的取值范围,导致最终的返回值错误。
首先,1e9+7是一个大的数,int32位的最大值为2147483647,所以对于int32位来说1000000007足够大。
int64位的最大值为2^63-1,对于1000000007来说它的平方不会在int64中溢出所以在大数相乘的时候,
因为(a∗b)%c=((a%c)(b%c))%c,所以相乘时两边都对1000000007取模,再保存在int64里面不会溢出。

思路分析: 💫

说到递归大家肯定再详细不过了,我在这里也简单的说一下。
原理:把 f(n) 问题的计算拆分成f(n−1) 和 f(n−2) 两个子问题的计算,并递归,以 f(0) 和 f( 1) 为终止条件。
缺点: 大量重复的递归计算,例如 f(n) 和 f(n−1) 两者向下递归需要 各自计算f(n−2) 的值容易超时。【我试了一下在这个范围(0 <= n <= 37)保证答案是一个32位整数,即 answer <= 2^31 - 1 】
图解:
在这里插入图片描述

代码展示: 👇

   class Solution {
        public int fib(int n) {
            int mod = 1000000007;   //题目有要求要取模
            if (n <= 1) {
                return n;
            }
            return(fib(n - 1) + fib(n - 2)) % 1000000007;
        }
    }

以上算法实现的复杂度分析:

  • 时间复杂度是: O(2 ^n),每一层的节点数是上一层的两倍,一共需要遍历( 2 ^n) 个节点。
  • 空间复杂度是 : O(n),一共需要遍历 n层,所以,递归栈需要占用 O(n)的额外空间。

这里有一些递归的基础题👉题型链接👈

🏠解法二、递归实现动态规划

也就是记忆化搜索,创建一个 memo 数组用于防止重复计算
原理: 在递归法的基础上,新建一个长度为( n +1)的数组,用于在递归时存储 f(0) 至 f(n) 的数字值,重复遇到某数字则直接从数组取用,避免了重复的递归计算
缺点: 记忆化存储需要使用O(N) 的额外空间

可以选择从上到下,也可以从下到上(容易理解)

代码展示:(从下到上)👇

class Solution {
    public int fib(int n) {
        if (n == 0) {
            return 0;
        }
        int[] memo = new int[n+1];  //新建一个长度为 n+1 的数组
        memo[0] = 0;
        memo[1] = 1;
        for (int i = 2; i <= n; i++) {
            memo[i] = memo[i-1] + memo[i-2];   //重复遇到某数字则直接从数组取用,避免了重复的递归计算。
            memo[i] %= 1000000007;
        }
        return memo[n];
    }
}

以上算法实现的复杂度分析:

  • 时间复杂度是: O(n)
  • 空间复杂度是 : O(1)

🏘️解法三、迭代实现动态规划

题目已经给出来递推公式: F(N) = F(N - 1) + F(N - 2)
所以直接先定义三个变量进行循环就可以了
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

代码展示: 👇

class Solution {
    public int fib(int n) {
        int a = 0, b = 1, sum;   //定义三个变量
        for(int i = 0; i < n; i++) {  
            sum = (a + b) % 1000000007;   
            a = b;                 //这三步实现递推
            b = sum;
        }
        return a;
    }
}

以上算法实现的复杂度分析:

  • 时间复杂度是: O(n)
  • 空间复杂度是 : O(1)

解法三应该好理解,复杂度也不错,但是也不是最好的,这道题应该快速幂是最优解,下面整一个好玩的,我相信大家都看得懂😄

class Solution {     //二话不说直接打表 (~ ̄▽ ̄)~ 
    public int fib(int n) {
        int[] map = {
                0, 1, 1, 2, 3, 5, 8, 13, 21, 34,
                55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181,
                6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229,
                832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986,
                102334155, 165580141, 267914296, 433494437, 701408733, 134903163, 836311896, 971215059, 807526948, 778742000,
                586268941, 365010934, 951279875, 316290802, 267570670, 583861472, 851432142, 435293607, 286725742, 722019349,
                8745084, 730764433, 739509517, 470273943, 209783453, 680057396, 889840849, 569898238, 459739080, 29637311,
                489376391, 519013702, 8390086, 527403788, 535793874, 63197655, 598991529, 662189184, 261180706, 923369890,
                184550589, 107920472, 292471061, 400391533, 692862594, 93254120, 786116714, 879370834, 665487541, 544858368,
                210345902, 755204270, 965550172, 720754435, 686304600, 407059028, 93363621, 500422649, 593786270, 94208912,
                687995182
        };
        return map[n];
    }
}

相关文章:
上一篇:【剑指offer】之《替换空格》
下一篇:【剑指offer】之《二进制中1的个数》

🌈我的感受:

  斐波那契数列想必是大家刚开始编程时都会接触到的一道经典题目,它的特性非常符合计算机思维。但是这道题看似小巧,但暗藏玄机,解法多样,即考验基础知识,又可以拓展成更困难的题目,比如,朴素递归拓展可以变为动态规划,记忆化搜索可以考察DFS/BFS/有向图/邻接矩阵/拓补排序,朴素迭代可以考察链表/二叉树的基本用法,很多与动态规划相关的问题也是从迭代入手,而滚动矩阵又往往是常见的优化方案。前期我能力有限,后期我会逐级完善解法,除了上述的四种方法外,这道题也是练手快速幂的好题目。所以,这是一道非常有趣的题。
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小奕vi

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

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

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

打赏作者

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

抵扣说明:

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

余额充值