目录

1、前言

2、什么是递归

2.1 一则故事

2.2 递归的概念

2.3 递归策略

2.4 递归的两个必要条件

3、递归代码举例

3.1 n的阶乘(递归实现)

3.2 数字之和

3.3 斐波那契数

3.3.1 递归方法实现

3.3.2 迭代(循环)方式实现

4、总结


1、前言

本期我们共同来研究一下递归这一概念,如果你C语言有关这方面的知识已经学过了,那么建议跳过这一节,因为思想是一致的,只不过用Java的代码语言来实现而已。如果你没有听说过递归这个概念,那么你要好好看完这篇博客,相信你会有所收获的。

2、什么是递归

2.1 一则故事

那么在开始讲解之前,我们首先要明白,什么是递归,有这样一则故事:

"从前有坐山,山上有座庙,庙里有个老和尚给小和尚将故事,讲的就是:
"从前有座山,山上有座庙,庙里有个老和尚给小和尚讲故事,讲的就是:
"从前有座山,山上有座庙..."
"从前有座山……"

  • 上面这个故事中:自身中又包含了自己,该种思想在数学和编程中非常有用,因为有些时候,我们遇到的问题直接并不好解决,但是发现将原问题拆分成其子问题之后,子问题与原问题有相同的解法,等子问题解决之后,原问题就迎刃而解了。

2.2 递归的概念

  • 程序调用自身的编程技巧称为递归( recursion)。
  • 递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。

2.3 递归策略

  • 只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。
  • 递归的主要思考方式在于:把大事化小。

2.4 递归的两个必要条件

  • 存在限制条件,当满足这个限制条件的时候,递归便不再继续。
  • 每次递归调用之后越来越接近这个限制条件
  • 注意:其实在递推中,比较重要的是找到递推公式,这是递推实现的第一步,递推的代码逻辑很复杂,很难想象真正代码运行的逻辑。

3、递归代码举例

3.1 n的阶乘(递归实现)

  • 在码代码之前,我们首先要想清楚这个功能的递推公式,如:3!= 3*2*1;5!= 5*4*3*2*1;依次类推,我们不难发现他的递推公式是: N!= N*(N-1)。
  • 其次我们要给定一个限制条件,当满足这个限制条件的时候,递归便不再继续。在这个代码中种也很好想到,当N每次-1的,都在向着最终的条件:N = 1靠近,最终减到1的时候,就return 1即可,即代码不用在继续执行了,所以在这个代码中,N = 1就是限制条件,且每次代码运行,都能越来越接近这个限制条件。
  • 递归的2个必要条件满足之后,代码如下:
public static void main(String[] args) {int n = 3;int ret = factor(n);System.out.println("ret = " + ret);
}
public static int factor(int n) {if (n == 1) {return 1;
}return n * factor(n - 1); // factor 调用函数自身
}
// 执行结果
ret = 6
  • 鉴于这是本章第一个代码,我会详细分析一下它的代码运行逻辑,还是强调一下,递归的代码运行逻辑十分复杂,还请不要在意一些细节,能找到我上面所说的几点关键点即可。
  • 递归分为两个步骤,分别是:    和   
  • :递就是向后传递的过程,为方便大家理解,下图中红色的部分为递的过程,这里我们的N取3,也就是计算3!= 3*2*1这个过程。其中开始时N = 3,执行n*fac(n-1)的代码,这里面的n为3,但是我们发现后面再一次调用了fac函数,也就是自己函数内部调用自己,这就是递归,我们不会计算后面fac(n-1)的值,当然return这一行代码也同样不会执行,我们会在最后归的时候统一计算这两部分的内容
  • 此时n-1之后n = 2,带入到fac函数中去再次执行函数中的代码,会重复上一步递的内容。此时:n = 2;fac(1),同样的fac和return不会被计算出数值;
  • 直到计算完上一步的时候N = 1;此时代码执行到了限制条件的位置,那么递的步骤结束,马上进入归的部分,此时n = 1,代码执行return 1。
  • :终于千辛万苦到了归的地方,为了方面理解,绿色的线条为归的部分。还记得我们在递的时候fac和return的值都没有计算出来吗,在归的部分,我们都要归回来;
  • 上面已经说明了代码执行到了return 1 的部分,return 1 的值也就是第二张图fac(1)的值,这里我们计算tmp = n * fac(1) = 2 * 1;这个代码,return值为2,再执行return tmp的代码,即把return 2的值赋值给到了fac(2);
  • 接着计算第一张图的tmp = n * fac(2) = 3 * 2;这回return的值为6,至此代码全部结束。

  • 下面的流程图更加方便大家理解,递归是按照编号从1~8进行执行。

注意:
在递归中,我们只需要找到递推的公式,以及满足递归的两个必要条件即可,代码是如何去运行的个人认为不在我们考虑的范围内。

  • 接下来的代码我只会写明递推公式和限制条件,分析的逻辑大同小异,请参照代码1的分析逻辑,如果大家有不懂的地方,欢迎私信或者留言给我。

3.2 数字之和

  • 写一个递归方法,输入一个非负整数,返回组成它的数字之和(如123  :1+2+3)
  • 递推公式:
(n % 10) + numSum(n/10)
  • 限制条件:因为n < 10 的时候返回自己即可,不用每一位相加。
n < 10
  • 代码实现:
  public static void main(String[] args) {System.out.println(numSum(12));}public static int numSum(int n){if(n < 10){return n;}else{int sum = (n % 10) + numSum(n/10);return sum;}}

3.3 斐波那契数

3.3.1 递归方法实现

  • 这里关于什么是斐波那契数列就不过多介绍了,详情可以百度一下,斐波那契数列的递推公式是固定的。
  • 递推公式:
fib(n-1) + fib(n-2)
  • 限制条件:我们这里默认第一位和第二位都是1。
n == 1 || n == 2
  • 代码实现:
 public static void main(String[] args) {System.out.println(fib(41));}public static int fibRec(int n){if(n == 1 || n == 2){return 1;}else{int tmp = fib(n-1) + fib(n-2);return tmp;}}

注意:

  • 当我们求 fib(40) 的时候发现, 程序执行速度极慢,原因是进行了大量的重复运算,即当我们计算fib(3)的时候就将fib(2)计算过了,但是fib(4)的时候我们又一次计算了fib(2),这就出现了重复计算的情况,使代码效率大大降低。

  • 所以我们发现并不是所有的代码都适合用递归的方式,那么上面的代码我们用普通循环的方式再来实现一遍。

3.3.2 迭代(循环)方式实现

  • 我们用循环的方式实现斐波那契数,可以看到此时代码的效率大大增加了,不再存在重复计算的代码。
 public static void main3(String[] args) {System.out.println(fib(41));}public static int fib(int n){if(n == 1 || n == 2){return 1;}else{int fib1 = 1;int fib2 = 1;int fib3 = 0;for (int i = 3; i <= n ; i++) {fib3 = fib1 + fib2;fib1 = fib2;fib2 = fib3;}return fib3;}}

4、总结

  1. 许多问题是以递归的形式进行解释的,这只是因为它比非递归的形式更为清晰
  2. 但是这些问题的迭代实现往往比递归实现效率更高,虽然代码的可读性稍微差些。
  3. 当一个问题相当复杂,难以用迭代实现时,此时递归实现的简洁性便可以补偿它所带来的运行时开销。
  4. 至此,我们的递归的介绍就结束了,如果大家有什么不明白的地方,欢迎大家私信或者留言给我,我会一一回复!

Java代码实现—函数递归相关推荐

  1. 【Java代码实现】递归两大经典问题-----“汉诺塔问题” 与 “青蛙跳台阶问题” 讲解

  2. java 什么时候用递归_如果要用Java实现算法,一定慎用递归

    现象 : 递归是我们很经典的一种算法实现,可以很好的描述一个算法的原理!对于算法的描述.表现和代码结构理解上,递归都是不错的选择! 但是本文想说的是java实现一个递归算法的时候尽量不要用递归实现,而 ...

  3. java 7.函数-递归_带有谓词的Java中的函数样式-第1部分

    java 7.函数-递归 您一直在听到将要席卷全球的函数式编程,而您仍然坚持使用普通Java? 不用担心,因为您已经可以在日常Java中添加一些功能样式. 此外,它很有趣,可以节省许多代码行并减少错误 ...

  4. java 7.函数-递归_带有谓词的Java中的函数样式-第2部分

    java 7.函数-递归 在本文的第一部分中,我们介绍了谓词,这些谓词通过具有返回true或false的单一方法的简单接口,为Java等面向对象的语言带来了函数式编程的某些好处. 在第二部分和最后一部 ...

  5. java基础(11)函数重载以及函数递归求和

    函数重载 在Java类中,可以声明多个方法名相同的方法,但是参数类型和个数不同,相同的话就成了相同的方法声明了两次肯定报错,我们可以利用重载的特性,编写多个同名函数,实现类似的功能. 和修饰符 pub ...

  6. Android Studio编写运行测试纯java代码可带main()函数

    问题 小伙伴们在做安卓项目的时候,是不是有时候会忘记某些api的使用方法,不太确定他们的结果是怎样的,需要写一些测试代码,验证看看我们的写法是否正确.刚开始的时候我是在页面上写一个Button,添加点 ...

  7. python中的代码复用与函数递归

    代码复用与模块化设计 所谓的代复用,顾名思义,也就说同一份代码在需要的时候可以被重复使用,有两种形式:一个是函数,一个是对象. 模块化设计,简单地来讲,就是通过函数或对象封装将程序划分为模块及模块间的 ...

  8. 扫描java类文件_java递归与非递归实现扫描文件夹下文件的实例代码

    java递归与非递归实现扫描文件夹下所有文件 java扫描指定文件夹下面的所有文件,供大家参考,具体内容如下 扫描一个文件夹下面的所有文件,因为文件夹的层数没有限制可能多达几十层几百层,通常会采用两种 ...

  9. 递归学习 斐波那契 java代码实现

    文章目录 java代码 单元测试 java代码 package csdn.dreamzuora.recursion;/*** Title: 斐波那契额* Description:*斐波那契数列:0.1 ...

最新文章

  1. 杨植麟:28 岁青年科学家,开挂人生的方法论
  2. uiautomator环境搭建所遇问题汇总
  3. python哪本好-python哪本书好
  4. Delphi组件开发-在窗体标题栏添加按钮
  5. 数据库操作技术--Spring jdbcTemplate
  6. 二叉树的建立与遍历(先中后层序)
  7. HTML 常用标签演示
  8. 【UOJ#33】【UR #2】树上GCD(长链剖分/根号分类讨论)
  9. Linux之系统操作命令
  10. hadoop的mapreduce知识点
  11. 为何只能在其关联的线程内启动timer?
  12. linux小程序实验报告,linux 小程序分析
  13. DEM数据(12米)精度获取方法
  14. kaldi_源码笔记 thchs-30_data_prep
  15. C++复习(二)——绘制余弦函数图像
  16. 阿里 vs. 腾讯,谁的收购更有眼光?
  17. 魔板游戏java_java魔板游戏 动物换位 俄罗斯方块
  18. Unity官方中文版(有生之年终于等到了)
  19. 2010考研数学二第(11)题——高阶导数
  20. VS2017 MFC对话框程序打印及打印预览的实现

热门文章

  1. 精通Windows API(一)Windows应用程序开发入门
  2. zypper 删除mysql_Zypper软件包管理教程----(一)简介及软件查询
  3. android bitmap显示图片,Android_07 Android中Bitmap加载图片
  4. UML视图---序列图和协作图
  5. 还没获得敬业福?快扫这个福立得万能福与敬业福,欧力给
  6. 基于STM32设计的老人监护系统
  7. 连微信也封不住的私域流量大爆发,商家该如何把握好时代风口,系统理解把握私域流量风口!...
  8. 制作HTML5游戏2
  9. 华为2018届校招勇敢星实习生招聘笔试+面试经历
  10. Kali上巡风扫描器的安装和使用