斐波那契数列(Java语言)

本文介绍了三种计算斐波那契数F(n)的方法:递归、动态规划和公式法。递归虽然简洁但效率低下,动态规划通过存储中间结果避免重复计算,空间复杂度为O(n);进一步优化的空间复杂度为O(1)的滚动数组法。公式法利用数学公式直接计算,避免了递归和循环。文章强调了算法效率和优化的重要性,并与LeetCode上的其他相关题目进行类比。
摘要由CSDN通过智能技术生成

LeetCode 509 斐波那契数

LeetCode链接
斐波那契数(通常用F(n)表示)的定义:

	F(0)=0,F(1)=1,
	F(n)=F(n-1)+F(n-2),n>1
	由斐波那契数构成的序列称为斐波那契数列,它由0,1开始(正宗的斐波那契数列应该是以1,1开始,没有F(0)这一项),之后的每一项都是前两项之和。

题目要求给定一个数n,计算F(n)的值。(0<=n<=30)

斐波那契数列因数学家莱昂纳多·斐波那契以兔子繁殖为例子而引入,故又称为“兔子数列”。
一般而言,兔子在出生两个月后,就有繁殖能力,一对兔子每个月能生出一对小兔子来。如果所有兔子都不死:
我们不妨拿新出生的一对小兔子分析一下:
第一个月小兔子没有繁殖能力,所以还是一对
两个月后,生下一对小兔对数共有两对
三个月以后,老兔子又生下一对,因为小兔子还没有繁殖能力,所以一共是三对
------
依次类推可以列出下表:

  所以能得出以下公式:

幼仔对数=前月成兔对数
成兔对数=前月成兔对数+前月幼仔对数=前月总体对数
总体对数=本月成兔对数+本月幼仔对数=前月总体对数+前月成兔对数=前月总体对数+前前月总体对数    //F(n)=F(n-1)+F(n-2)的来源
//希望这几个公式没有把你绕晕

方法一、递归

  斐波那契序列是典型的递归问题。除了头两项以外,其他F(n)的值取决于F(n-1),F(n-2)。而F(n-1),F(n-2)又取决于它们前两项的值,以此类推,在此我就不过多赘述。代码如下:

//递归
class Solution {
   	public int fib(int n) {
    	if(n<2) {
         	return n;
    	}
    	return fib(n-1)+fib(n-2);
    }
}

方法二、动态规划

  递归的代码虽然简捷明了,看起来赏心悦目,但函数调用的次数很多,开销很大,而且它里面进行了很多重复计算。举个例子 ,如下图,计算F(9)时,F(7)要计算2次,F(6)要计算3次…n越大,重复计算的次数就越多,时间复杂度为O(2^n),指数爆炸,这并不可取。
在这里插入图片描述

  如果将每次计算的F(n)存起来,那么将会大大减少计算的次数,这就是动态规划的思想。状态转移方程就是F(n)=F(n-1)+F(n-2),边界条件就是F(0)=0,F(1)=1,用dp[n]存储F(n)。代码如下:

class Solution {
    public int fib(int n) {
    	if(n<2) {
        	return n;
    	}
    	int[] dp=new int[n+1];
    	//边界条件,dp数组的初始化
    	dp[0]=0;
    	dp[1]=1;
    	for(int i=2;i<=n;i++){
    		dp[i]=dp[i-1]+dp[i-2];
    	}
    	return dp[n];
    	}
}

因为最后结果只要求返回F(n),而F(n)只与F(n-1)、F(n-2)有关,所以,上面空间复杂度为O(n)的方法还可以优化,利用滚动数组的思想,用两个变量存储F(n-1)、F(n-2),将空间复杂度降为O(1)。代码如下:

//动态规划
class Solution {
	public int fib(int n) {
        if(n<2) {
            return n;
        }
        //p,q是边界F(0)、F(1)
        int p=0;
        int q=1;
        int res=0;
        for(int i=1;i<n;i++){
        	res=p+q;
            p=q;
            q=res;
        }
        return res;
    }
}

方法三、公式法

  这里直接粘贴官方题解中有关公式法的介绍
在这里插入图片描述
  第一次看完我大受震憾,只能说自己实力不够还得好好学习吧。代码如下:

	class Solution {
    	public int fib(int n) {
        	double temp=Math.sqrt(5);
        	double ans=(1/temp)*(Math.pow((1+temp)/2,n)-Math.pow((1-temp)/2,n));
        	return (int)Math.round(ans);
        	//round()函数参数为double型时,返回一个最接近该参数的long型数,参数为float型时,返回一个最接近的int值
    	}
	}

类比

  该题与LeetCode 70 题 爬楼梯 LeetCode链接 十分相似。同时,LeetCode 剑指offer 10-I 斐波那契数列 LeetCode链接 中有当n很大时关于取模的处理,都可以尝试做一下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值