【Java实现】剑指Offer10.1/10.2——斐波那契数列/青蛙跳台阶:三种思路分享

在这里插入图片描述
同学们,下方传送门:青蛙跳台阶的问题


首先看到这道题后,我们能够自己继续递推:

0112358...

同时,能够得到n,n-1,n-2之间的关系:

F(n)=F(n-1)+F(n-2)

再配合上面给到的初始条件,第一种思路也就产生了——递归。

思路1:递归

想到递归,先找到构建递归的条件:

  1. 终止条件
  2. 本问题和子问题解的关系

以上问题,我们都有答案,直接上代码:

class Solution {
    public int fib(int n) {
        if(n==0) return 0;
        if(n==1) return 1;
        return fib(n-1)+fib(n-2);
    }
}

递归构建非常清晰,不过很遗憾,答案超时

为什么超时呢?

这是因为斐波那契数列特殊的机制:
F(n)=F(n-1)+F(n-2);
F(n-1)=F(n-2)+F(n-3);
F(n-2)=F(n-3)+F(n-4);

能够看出,子问题的解答过程中,有很多多余的计算过程,也就是很多结果之前算过了。

结合时间复杂度分析:
假设想求F(3),需要计算下层所有的值。
相当于一个三层的完全二叉树,求其所有节点个数,因此时间复杂度为O(2^n)

因此就有了我们下面两种基于迭代(循环的方法)


思路2:带中间变量的迭代

上面这种方法的问题在于,中间步骤经历了太多次重复计算。

那么如果我们能够将计算过的结果重复利用,就能很好地舍去计算的过程,就能大大提高性能

上代码:

class Solution {
    public int fib(int n) {
        if(n<=1) return n;

        int a=1;
        int b=0;
        int ans=0;

        for(int i=2;i<=n;i++) {
            ans=(a+b)%1000000007;
            b=a;
            a=ans;
        }
        return ans;
    }
}

跟递归一样,我们开始进行一个基本的判断。
对于大于1的数,我们迭代来求最终的结果
由于斐波那契数列的性质不变:F(n)=F(n-1)+F(n-2);

因此我们用两个变量,来记录一直变化的那两个加数

如上代码中:
第一次for循环,计算的是F(2),我们都知道F(2)=F(1)+F(0),而变量a刚好是F(1),b刚好是F(0)

重点来了!!!!

通过一轮计算后,我们需要让a表示F(n-1),b表示F(n-2),因此在计算下一轮之前,把F(2)的值赋值给a,把之前a的值赋值给b,再继续下轮循环

这种方法,将他的时间复杂度从O(2^n),变成了O(n),只是for循环的次数


思路3:利用数组存储数据,迭代

这个解法的整体思路和上一种很相似,只不过把变量变成了数组
当遇到已经计算过的数据的时候,直接去数组中取即可!

代码如下:

class Solution {
    public int fib(int n) {
        if(n<2) {
            return n;
        }
        int[] ans=new int[n+1];
        ans[0]=0;
        ans[1]=1;
        for(int i=2;i<=n;i++) {
            ans[i]=(ans[i-1]+ans[i-2])%1000000007;
        }
        return ans[n];
    }
}

由于有数组的存在,这种方法的空间复杂度变成了O(n)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值