斐波那契数列的定义:

斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家列安纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、.....在数学上,斐波那契数列以如下递归的方法定义:F(1)=1,F(2)=1,F(n)=F(n-1)+F(n-2)(n>=2,n∈N*)

翻译成Java代码是:

  public int Fibonacci(int n) {
if (n == 1 || n == 2) {
return 1;
}
return Fibonacci(n - 1) + Fibonacci(n - 2);
}

代码很简单,简单的递归。

但是这个方法在实际执行的过程中可能出现java.lang.StackOverflow错误,这个错误是什么意思呢?jdk官方文档的解释:应用程序递归太深二发生堆栈溢出时,抛出该错误。

Java程序运行时,会在内存中划分5片空间进行数据的存储。分别是:1、寄存器;2、本地方法区;3、方法区;4、栈;5、堆。我们上面说的堆栈指的就是栈,java栈中主要存储基本数据类型的值和对象的引用。那么在上面这个递归执行的过程中发生了什么呢?

我们先分析一下这个数列的公式:

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

  =(F(n-2)+F(n-3))+(F(n-3)+F(n-4))

  =F(n-3)+F(n-4)+F(n-4)+F(n-5)+F(n-4)+F(n-5)+F(n-5)+F(n-6)

  ......

观察可以发现,这个方法在执行的过程中每多增加一层递归需要存储临时参数的栈空间就是上一层的两倍,而在最终结束前,每一层的方法参数n的值都不会被释放(出栈),所以栈的深度将会以O(2^n)的空间复杂度越压越深,最终达到醉倒深度导致栈溢出。

毫无疑问,对实现一个斐波那契数列来说,这个实现空间复杂度O(2^n)太大了,而实际上每一层方法传入的参数值n在成功传到下一层的时候就没用了,但是却一直不能出栈,导致了栈空间的浪费。

所以要优化这个算法,我们就只能让每层方法借宿后及时出栈,也就是别用递归。不用递归用什么呢?

很多时候,递归的功能可以通过循环来实现,循环内部的代码看恶意看做是一个方法的方法体,与递归不同的地方是循环代码没有返回值,但我们可以通过循环内部更改外部变量的值来实现类似于返回值的效果。有公式F(n)=F(n-1)+F(n-2),且已知F(1)=1和F(2)=1,F(3)=1+1=2,F(4)=F(3)+F(2)=2+1=3,利用这个思路,我们可以得到如下代码:

  public int Fibonacci(int n) {
int l = 1, j = 1, k = 1;
for (int i = 3; i <= n; i++) {
k = l + j;
l = j;
j = k;
}
return n == 0 ? 0 : k;
}

解释一下: l 和 j 一开始分别为F(1)和F(2)的值,k为返回的值,循环从n>2时开始(小于等于2直接返回k的初始值),没循环一层,k的值变为前两项 l 和 j 的和,然后更新 l 和 j 的值供下次循环使用。

改良后的算法最大的特点就是栈深度只有一层,没有无用的变量值浪费空间,空间复杂度达到了最优。


原文链接:https://www.cnblogs.com/JackPn/p/9383450.html

(转)从斐波那契数列看Java方法的调用过程的更多相关文章

  1. 从斐波那契数列看java方法的调用过程

    先看斐波那契数列的定义: 斐波那契数列(Fibonacci sequence),又称黄金分割数列.因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为 ...

  2. Java实现斐波那契数列的多种方法

    小编综合了很多算法相关的书籍以及其他,总结了几种求斐波那契数列的方法 PS:其中的第83行的递归法是求斐波那契数列的经典方法 public class 斐波那契数列 { //迭代法 public st ...

  3. 算法小节(一)——斐波那契数列(java实现)

    看到公司的笔试题中有一道题让写斐波那契数列,自己忙里偷闲写了一下 什么是斐波那契数列:斐波那契数列指的是这样一个数列 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...

  4. 【斐波那契数列】java探究

    题目描述 大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0). n<=39 解析 (1)递归方式 对于公式f(n) = f(n-1) + f(n ...

  5. 几种复杂度的斐波那契数列的Java实现

    一:斐波那契数列问题的起源 13世纪初期,意大利数论家Leonardo Fibonacci在他的著作Liber Abaci中提出了兔子的繁殖问题: 如果一开始有一对刚出生的兔子,兔子的长大需要一个月, ...

  6. 斐波那契数列【java实现】

    java 实现斐波那契数列 以下是Java代码实现(递归与递推两种方式): import java.util.Scanner; /** * Fibonacci * * @author tongqian ...

  7. 斐波那契数列(Java)

    一.什么是斐波那契数列 斐波那契数列(Fibonacci sequence),又称黄金分割数列.因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为& ...

  8. 剑指Offer-7.斐波那契数列(C++/Java)

    题目: 大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0). n<=39 分析: 斐波那契数列是0,1,1,2,3,5,8,13...也就是当前 ...

  9. 剑指offer第二版面试题10:斐波那契数列(JAVA版)

    题目:写一个函数,输入n,求斐波那契数列的第n项.斐波那契数列的定义如下: 1.效率很低效的解法,挑剔的面试官不会喜欢 使用递归实现: public class Fibonacci { public ...

随机推荐

  1. MySQL数据库同步工具的设计与实现

    一.背景 在测试过程中,对于不同的测试团队,出于不同的测试目的,我们可能会有多套测试环境.在产品版本迭代过程中,根据业务需求,会对数据库的结构进行一些修改,如:新增表.字段.索引,修改表.字段索引等操 ...

  2. Dubbo架构与底层实现

    一.Dubbo的设计角色 (1)系统角色Provider: 暴露服务的服务提供方.Consumer: 调用远程服务的服务消费方.Registry: 服务注册与发现的注册中心.1Monitor: 统计服 ...

  3. MATLAB学习(二)读写xls文件

    >> N=xlsread('DRINK.xls','DRINK','A1:D8') N = 207.2000 3.3000 15.5000 2.8000 36.8000 5.9000 12 ...

  4. 用R语言求置信区间

    用R语言求置信区间 用R语言求置信区间是很方便的,而且很灵活,至少我觉得比spss好多了. 如果你要求的只是95%的置信度的话,那么用一个很简单的命令就可以实现了 首先,输入da=c(你的数据,用英文 ...

  5. Linux批量处理常用方式

    批量处理思路在工作中使用的频率比较高,比如批量清理进程.批量删除文件.批量机器执行脚本等. 一.批量清理带java字样的进程 方式1:使用shell while语法. ${line}; done sh ...

  6. playbook常用操作

    playbook常用操作 1.检查playbook语法错误 ansible-playbook -i hosts deploy_coredns.yaml --syntax-check 2.查看playb ...

  7. UniEAP UTF 用户手册 (引擎)

    目录 第1章 概述 5 1.1 术语解释 5 第2章 测试文件组织 6 2.1 测试执行文件详解 7 2.1.1 参数配置 7 2.1.2 测试报告配置 9 2.1.3 浏览器类型配置 9 2.1.4 ...

  8. AES 加密算法的原理详解

    AES 加密算法的原理详解 本教程摘选自 https://blog.csdn.net/qq_28205153/article/details/55798628 的原理部分. AES简介 高级加密标准( ...

  9. Base64加密 解密

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  10. JavaScript基本入门02

    目录 JavaScript基础入门 02 条件语句 if 语句 if .. else 语句 switch 结构 循环语句 while 循环 continue 关键字 do...while语句 for ...