递归:指在当前方法内调用自己的这种现象。

概述

public static void a(){a();
}

递归

  • 直接递归:自己的方法调用自己。
  • 间接递归:自己的方法调用别的方法,别的方法又调用自己。

案例

已知:f(x) = f(x-1) + 1 (恒等式)

已知:f(1) = 1

求: f(10) = ?

分析:
当x=5时,f(5) = f(5-1) + 1 = f(4) + 1,可发现如下规律:
f(5) = f(4) + 1
f(4) = f(3) + 1
f(3) = f(2) + 1
f(2) = f(1) + 1
f(1) = 1//代码实现:
@Test
void test02() {//计算 f(10)int res = f(10);System.out.println("计算结果: f(10) = "+res);//计算结果: f(10) = 10
}
//递归:
public int f(int x){if(x == 1){return 1;}else {return f(x-1)+1;}
}

递归的三要素

  1. 递归的公式。 f(x) = f(x-1) + 1
  2. 递归的终结点。f(1) = 1
  3. 递归的方向必须走向终结点。

递归累和

计算1 ~ n的和

分析:num的累和 = num + (num-1)的累和,所以可以把累和的操作定义成一个方法,递归调用。

实现代码

public class DiGuiDemo {public static void main(String[] args) {//计算1~num的和,使用递归完成int num = 5;// 调用求和的方法int sum = getSum(num);// 输出结果System.out.println(sum);}/*通过递归算法实现.参数列表:int 返回值类型: int */public static int getSum(int num) {/* num为1时,方法返回1,相当于是方法的出口,num总有是1的情况*/if(num == 1){return 1;}/*num不为1时,方法返回 num +(num-1)的累和递归调用getSum方法*/return num + getSum(num-1);}
}

小贴士:递归一定要有条件限定,保证递归能够停止下来,次数不要太多,否则会发生栈内存溢出。

递归求阶乘

  • 阶乘:所有小于及等于该数的正整数的积。
n的阶乘:n! = n * (n-1) *...* 3 * 2 * 1

分析:这与累和类似,只不过换成了乘法运算,学员可以自己练习,需要注意阶乘值符合int类型的范围。

推理得出:n! = n * (n-1)!

代码实现

public class DiGuiDemo {//计算n的阶乘,使用递归完成public static void main(String[] args) {int n = 3;// 调用求阶乘的方法int value = getValue(n);// 输出结果System.out.println("阶乘为:"+ value);}/*通过递归算法实现.参数列表:int 返回值类型: int */public static int getValue(int n) {// 1的阶乘为1if (n == 1) {return 1;}/*n不为1时,方法返回 n! = n*(n-1)!递归调用getValue方法*/return n * getValue(n - 1);}
}

问题

递归其实是有开销的,而且使用不当,可能会出现意外的结果,比如说这个调用:

System.out.println(getValue(100000));//StackOverflowError

系统并不会给出任何结果,而会抛出异常。

优化

递归函数经常可以转换为非递归的形式,通过循环实现。比如,求阶乘的例子,其非递归形式的定义是:

n! = 1 × 2 × 3 × … × n

这个可以用循环来实现,代码如下:

//使用for循环,计算阶乘
public long getValue2(int n){long result = 1;for (int i=1;i<=n;i++){result = result * i;}return result;
}
//使用 BigDecimal 优化
public BigDecimal getValue3(int n){BigDecimal result = BigDecimal.valueOf(1);for (int i=1;i<=n;i++){result = result.multiply(BigDecimal.valueOf(i));}return result;
}

递归啤酒问题

非规律化递归问题。

啤酒问题:啤酒2元一瓶,4个盖子可以换一瓶,2个空瓶(无盖子)可以换一瓶。

/*** 啤酒问题:啤酒2元一瓶,4个盖子可以换一瓶,2个空瓶(无盖子)可以换一瓶。*/
@Test
void mytest(){int i = 0;while (true){System.out.println("------------------------------- start - "+(++i)+" ---------------------------------");System.out.println("啤酒问题:啤酒2元一瓶,4个盖子可以换一瓶,2个空瓶(无盖子)可以换一瓶。");System.out.print("请输入购买金额:");int money;try {Scanner sc = new Scanner(System.in);money = sc.nextInt();if(money <= 0) {//输入小于等于0的数,退出系统System.err.println("bye bye!");break;}totalNums = lastEmptyBottleNums = lastLidNums = 0;//重置系统参数buyBeer(money);System.out.println(money+"元可以喝到【"+totalNums+"】瓶酒。");if(money % 2 > 0){System.out.println("找回零钱:"+money % 2+"元");}System.out.println("剩下的空瓶数量:"+lastEmptyBottleNums);System.out.println("剩下的盖子数量:"+lastLidNums);System.out.println("------------------------------- end - "+i+" ---------------------------------");System.out.println("\n\n");}catch (Exception e){System.err.println("\n您输入的字符不合法,请输入数字!!!");e.printStackTrace();}}}//定义全局变量:
//可以喝酒的总数
public int totalNums;
//剩下的空瓶数量
public int lastEmptyBottleNums;
//剩下的盖子数量
public int lastLidNums;
/*** 啤酒问题:啤酒2元一瓶,4个盖子可以换一瓶,2个空瓶(无盖子)可以换一瓶。*/
//买酒(换酒)方法
private void buyBeer(int money){//1、拿钱买酒//整数相除不是四舍五入,而是直接舍去小数位,例如:int i = 3/2 = 1//3元可以买一瓶酒,剩1元。5元可以买两瓶酒,剩1元。int number = money / 2;//总喝酒数量累加totalNums += number;//2、空瓶子、盖子兑换成钱//算出当前剩余的全部盖子喝瓶子数,换算成金额继续购买int curEmptyBottleNums = lastEmptyBottleNums + number;int curLidNums = lastLidNums + number;//兑换的钱,保存在变量exchangeMoney中int exchangeMoney = 0;//2个空瓶(无盖子)可以换一瓶酒//整数相除不是四舍五入,而是直接舍去小数位,例如:int i = 3/2 = 1//3个空瓶 = 两个换1瓶酒 + 剩一个空瓶,3/2=1瓶酒//5个空瓶 = 两个换1瓶酒 + 两个换1瓶酒 + 剩一个空瓶,5/2=2瓶酒//一瓶酒相当于2元,故(curEmptyBottleNums/2)要乘2exchangeMoney += (curEmptyBottleNums/2)*2;//算出剩余的瓶子//% 模运算。示例:5 % 2 = 2 + 2 + 1 = 模运算结果为1;6 % 2 = 2 + 2 + 2 + 0 = 模运算结果为0//空瓶子有5个 = 2个换一瓶酒 + 2个换一瓶酒 + 剩1个空瓶,5 % 2 = 1,5个空瓶换了两瓶酒剩1个空瓶lastEmptyBottleNums = curEmptyBottleNums % 2;//同理,计算盖子exchangeMoney += (curLidNums/4)*2;lastLidNums = curLidNums % 4;//3、继续购买(兑换)酒if(exchangeMoney >= 2){//2元一瓶酒buyBeer(exchangeMoney);}
}

运行结果:

------------------------------- start - 1 ---------------------------------
啤酒问题:啤酒2元一瓶,4个盖子可以换一瓶,2个空瓶(无盖子)可以换一瓶。
请输入购买金额:2
2元可以喝到【1】瓶酒。
剩下的空瓶数量:1
剩下的盖子数量:1
------------------------------- end - 1 ---------------------------------------------------------------- start - 2 ---------------------------------
啤酒问题:啤酒2元一瓶,4个盖子可以换一瓶,2个空瓶(无盖子)可以换一瓶。
请输入购买金额:5
5元可以喝到【3】瓶酒。
找回零钱:1元
剩下的空瓶数量:1
剩下的盖子数量:3
------------------------------- end - 2 ---------------------------------------------------------------- start - 3 ---------------------------------
啤酒问题:啤酒2元一瓶,4个盖子可以换一瓶,2个空瓶(无盖子)可以换一瓶。
请输入购买金额:0
bye bye!进程已结束,退出代码0

文件搜索

搜索D:\aaa 目录中的.java 文件。

分析

  1. 目录搜索,无法判断多少级目录,所以使用递归,遍历所有目录。
  2. 遍历目录时,获取的子文件,通过文件名称,判断是否符合条件。

代码实现

public class DiGuiDemo3 {public static void main(String[] args) {// 创建File对象File dir  = new File("D:\\aaa");// 调用打印目录方法printDir(dir);}public static void printDir(File dir) {// 获取子文件和目录File[] files = dir.listFiles();// 循环打印for (File file : files) {if (file.isFile()) {// 是文件,判断文件名并输出文件绝对路径if (file.getName().endsWith(".java")) {System.out.println("文件名:" + file.getAbsolutePath());}} else {// 是目录,继续遍历,形成递归printDir(file);}}}
}

对“文件搜索”进行优化,将查找结果保存在map中返回、设置排除目录、记录查询时间、查找到文件(程序)后如何执行(启动),见这篇博客。

小结

  • 递归是自己调用自己。
  • 递归如果控制的不恰当,会形成递归的死循环,从而导致栈内存溢出错误!!
  • 递归应该防止进入递归的死循环!

函数调用是有成本的,每一次调用都需要分配额外的栈空间用于存储参数、局部变量以及返回地址,需要进行额外的入栈和出栈操作。在递归调用的情况下,如果递归的次数比较多,这个成本是比较可观的,所以,如果程序可以比较容易地改为其他方式,应该考虑其他方式

栈的空间不是无限的,一般正常调用都是没有问题的,但如果栈空间过深,系统就会抛出错误java.lang.StackOverflowError,即栈溢出。

学习自B站深入学习Java编程-黑马、《Java编程的逻辑》-马俊昌

递归 - java实现相关推荐

  1. 归并排序(非递归,Java实现)

    归并排序(非递归):自底向上 public class MergeSort {/*** @param arr 待排序的数组* @param left 本次归并的左边界* @param mid 本次归并 ...

  2. leetcode 77. Combinations-排列|递归|非递归|Java|Python

    原题链接:77. Combinations [思路-Java.Python]递归实现 采用回溯算法.这是一道 NP 难问题,时间复杂度没办法提高,用一个循环递归处理子问题,问题的终止条件是每个组合中的 ...

  3. java 链表逆序 递归,java用递归和非递归实现链表逆序

    传统的逆序链表方法是使用三个指针来记录节点的状态,防止链表断裂. Node节点 public class Node { private int data; private Node next; pub ...

  4. 快排递归和非递归(java)

    思路:递归版思想没啥好说的,主要是细节,左右指针移动需要注意.这次尝试写非递归版,想的有点复杂了,其实类似树的前序遍历,细节见代码. import javafx.util.Pair; import j ...

  5. [Leedcode][第十题][剑指offer]面试题第[19]题[正则表达式][动态规划][递归][JAVA]

    [问题描述][困难] 请实现一个函数用来匹配包含'. '和'*'的正则表达式.模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(含0次).在本题中,匹配是指字符串的所有字符匹 ...

  6. 数据结构--汉诺塔--借助栈实现非递归---Java

    1 /*汉诺塔非递归实现--利用栈 2 * 1.创建一个栈,栈中每个元素包含的信息:盘子编号,3个塔座的变量 3 * 2.先进栈,在利用循环判断是否栈空, 4 * 3.非空情况下,出栈,检查是否只有一 ...

  7. 递归java程序_JAVA编程基础之递归结构

    递归结构 递归是一种常见的解决问题的方法,即把问题逐渐简单化. 递归的基本思想就是 自己调用自己 ",一个使用递归技术的方法将会直接或者间接的调用自己.利用递归可以用简单的程序来解决一些复杂 ...

  8. [剑指Offer]斐波那契数列、跳台阶、兔子数量问题(递归、非递归)(Java)

    剑指Offer题目 斐波那契数列 题目描述 [剑指Offer 7]大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0). [剑指Offer 8]一只青蛙 ...

  9. 二叉树中序遍历非递归Java

    问题来源与描述 问题来源:LeetCode 94,二叉树的中序遍历 思路 二叉树的中序遍历顺序是左,根,右. 使用递归比较简单: class Solution {public List<Inte ...

最新文章

  1. Eclipse不自动编译 设置后重新编译仍然没有编译
  2. 基于局域网的扩展认证协议EAPOL Extensible Authentication Protocol
  3. Java反射机制大神必学系列之 ,高级与低级的差别在哪里?
  4. javascript初学者必须注意的7个细节
  5. java中用字节流读取文本时中文乱码
  6. Execute SQL Task 参数和变量的映射
  7. 问题:连接查询和子查询的区别和连接及优劣?
  8. jquery中如何表达本页网址_如何用js得到当前页面的url信息方法(JS获取当前网址信息)...
  9. 利用CURL修改页面内容
  10. 吉米多维奇数学分析习题集学习指引
  11. SQL Server 2008 R2 学习总结
  12. 绝命毒师观后感(r6笔记第48天)
  13. python excel数据分析画直方图 饼状图_Excel数据可视化应用(直方图、折线图、饼状图)...
  14. 用户画像 用户画像表
  15. Python——验证码输入
  16. 在 Oracle Enterprise Linux 和 iSCSI 上构建您自己的 Oracle RAC 11g 集群
  17. Java准备工作【感谢狂神说JAVA】
  18. 微信小程序1.1.3
  19. 单细胞marker基因平均表达量热图
  20. Java——MD5密码加密

热门文章

  1. Seo新手入门,网络编辑如何写好文章(写文章技巧)
  2. apollo control模块中车身动力学模型推导
  3. Amazon S3 Storage(亚马逊的对象存储)
  4. Win10初次使用麦克风时该怎么设置才能发挥麦克风应有效果
  5. php景区景点在线票务售票管理系统
  6. 龙兵名片实时和访客对话需要开启WORKERMAN和对应的端口
  7. [轻笔记]C++实现类自注册时出现“multiple definition”(重定义)错误
  8. zynq7000 学习(二十四)VGA 接口原理分析和控制逻辑的实现学习
  9. 科普知识------地球上的水
  10. 强化学习论文分析4---异构网络_强化学习_功率控制《Deep Reinforcement Learning for Multi-Agent....》