斐波那契数列的第n个数(解决简单递归带来的效率问题)

剑指offer第9题

说起斐波那契数列,大家应该都很熟悉,并且很多人都能很熟练的写出递归代码.

如下,代码可谓很简洁

 public static int fabonacci(long n) {
        if(n<=0)
            return 0;
        if(n==1||n==2)
            return 1;
        return fabonacci1(n-1)+fabonacci1(n-2);
}

但是简单递归带来一个很严重的问题就是效率问题,他会计算很多重复的东西,比如计算f(9),需要计算f(8)和f(7),当计算f(10)的时候,并不知道f(9)和f(8)需要再次计算,效率很低,如图所示
在这里插入图片描述
那么解决办法呢,其实也很容易想到,就是把每次计算的值保存起来,当需要的时候先去查询,如果有直接拿来用即可,

当然存储也需要找到合适的数据结构,这里需要能够较快的进行增加和查询,并且需要根据一个索引找到一个值,会容易想到map,但是这里数组是更好的选择,因为数组本身就是一个简单的map,由它的下标和每个下标所存储的值就可以构成键值对,并且使用很容易.

代码如下
public class Question9 {
    private static int n=30;
    private static long[] arr=new long[n];  //存储每次递归得到的值
    private static int flag=0; //记录递归的次数
    public static void main(String[] args) {
        System.out.println("第n个值:"+fabonacci1(n));
        System.out.println("递归次数:"+flag);
    }
    //改进递归方法
    public static long fabonacci(int n) {
        flag++;
        long first;    //斐波那契数列的第n个数等于前两个数之和,first即指向第一个数
        long second; //指向第二个数
        if(n<=0)
            return 0;
        if(n==1||n==2)
            return 1;
        //这里用数组来保存每一次的值,第0位对应的是第1个值,第1位对应的是2个值,所以每次要多减1.
        //每次递归的时候先查询,没有再递归,同时递归之后把值存起来
        if(arr[n-3]==0){
            first=fabonacci(n-2);
            arr[n-3]=first;
        }else
            first=arr[n-3];
        if(arr[n-2]==0){
            second=fabonacci(n-1);
            arr[n-2]=second;
        }else
            second=arr[n-2];
        return first+second;
    }
    //简单递归
    public static int fabonacci1(long n) {
        flag++;
        if (n <= 0)
            return 0;
        if (n == 1 || n == 2)
            return 1;
        return fabonacci1(n - 1) + fabonacci1(n - 2);
    }
}

在以上代码中,用flag来表示递归调用的次数来进行对比,当n为30时,结果如下:

普通递归结果:
在这里插入图片描述
有保存值的递归方法结果:

在这里插入图片描述
可以看到,递归调用的次数差别很大,其实这种时间复杂度是以n的指数的方式递增的.

最后,如果单纯的想要求斐波那契数列的第n个值,还有另一种写法,直接用循环即可,定义两个变量,代表所求数的前两个数,比如计算f(3),第一个变量是f(2),第二个变量是f(1),当计算f(4)的时候,第一个变量更新为刚刚计算所得f(3),第二个变量更新为f(2),如此循环即可.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

程序员bling

义父,感谢支持

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

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

打赏作者

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

抵扣说明:

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

余额充值