递归 - java实现
递归:指在当前方法内调用自己的这种现象。
概述
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;}
}
递归的三要素:
- 递归的公式。 f(x) = f(x-1) + 1
- 递归的终结点。f(1) = 1
- 递归的方向必须走向终结点。
递归累和
计算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
文件。
分析:
- 目录搜索,无法判断多少级目录,所以使用递归,遍历所有目录。
- 遍历目录时,获取的子文件,通过文件名称,判断是否符合条件。
代码实现:
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实现相关推荐
- 归并排序(非递归,Java实现)
归并排序(非递归):自底向上 public class MergeSort {/*** @param arr 待排序的数组* @param left 本次归并的左边界* @param mid 本次归并 ...
- leetcode 77. Combinations-排列|递归|非递归|Java|Python
原题链接:77. Combinations [思路-Java.Python]递归实现 采用回溯算法.这是一道 NP 难问题,时间复杂度没办法提高,用一个循环递归处理子问题,问题的终止条件是每个组合中的 ...
- java 链表逆序 递归,java用递归和非递归实现链表逆序
传统的逆序链表方法是使用三个指针来记录节点的状态,防止链表断裂. Node节点 public class Node { private int data; private Node next; pub ...
- 快排递归和非递归(java)
思路:递归版思想没啥好说的,主要是细节,左右指针移动需要注意.这次尝试写非递归版,想的有点复杂了,其实类似树的前序遍历,细节见代码. import javafx.util.Pair; import j ...
- [Leedcode][第十题][剑指offer]面试题第[19]题[正则表达式][动态规划][递归][JAVA]
[问题描述][困难] 请实现一个函数用来匹配包含'. '和'*'的正则表达式.模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(含0次).在本题中,匹配是指字符串的所有字符匹 ...
- 数据结构--汉诺塔--借助栈实现非递归---Java
1 /*汉诺塔非递归实现--利用栈 2 * 1.创建一个栈,栈中每个元素包含的信息:盘子编号,3个塔座的变量 3 * 2.先进栈,在利用循环判断是否栈空, 4 * 3.非空情况下,出栈,检查是否只有一 ...
- 递归java程序_JAVA编程基础之递归结构
递归结构 递归是一种常见的解决问题的方法,即把问题逐渐简单化. 递归的基本思想就是 自己调用自己 ",一个使用递归技术的方法将会直接或者间接的调用自己.利用递归可以用简单的程序来解决一些复杂 ...
- [剑指Offer]斐波那契数列、跳台阶、兔子数量问题(递归、非递归)(Java)
剑指Offer题目 斐波那契数列 题目描述 [剑指Offer 7]大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0). [剑指Offer 8]一只青蛙 ...
- 二叉树中序遍历非递归Java
问题来源与描述 问题来源:LeetCode 94,二叉树的中序遍历 思路 二叉树的中序遍历顺序是左,根,右. 使用递归比较简单: class Solution {public List<Inte ...
最新文章
- Eclipse不自动编译 设置后重新编译仍然没有编译
- 基于局域网的扩展认证协议EAPOL Extensible Authentication Protocol
- Java反射机制大神必学系列之 ,高级与低级的差别在哪里?
- javascript初学者必须注意的7个细节
- java中用字节流读取文本时中文乱码
- Execute SQL Task 参数和变量的映射
- 问题:连接查询和子查询的区别和连接及优劣?
- jquery中如何表达本页网址_如何用js得到当前页面的url信息方法(JS获取当前网址信息)...
- 利用CURL修改页面内容
- 吉米多维奇数学分析习题集学习指引
- SQL Server 2008 R2 学习总结
- 绝命毒师观后感(r6笔记第48天)
- python excel数据分析画直方图 饼状图_Excel数据可视化应用(直方图、折线图、饼状图)...
- 用户画像 用户画像表
- Python——验证码输入
- 在 Oracle Enterprise Linux 和 iSCSI 上构建您自己的 Oracle RAC 11g 集群
- Java准备工作【感谢狂神说JAVA】
- 微信小程序1.1.3
- 单细胞marker基因平均表达量热图
- Java——MD5密码加密
热门文章
- Seo新手入门,网络编辑如何写好文章(写文章技巧)
- apollo control模块中车身动力学模型推导
- Amazon S3 Storage(亚马逊的对象存储)
- Win10初次使用麦克风时该怎么设置才能发挥麦克风应有效果
- php景区景点在线票务售票管理系统
- 龙兵名片实时和访客对话需要开启WORKERMAN和对应的端口
- [轻笔记]C++实现类自注册时出现“multiple definition”(重定义)错误
- zynq7000 学习(二十四)VGA 接口原理分析和控制逻辑的实现学习
- 科普知识------地球上的水
- 强化学习论文分析4---异构网络_强化学习_功率控制《Deep Reinforcement Learning for Multi-Agent....》