Java --> 方法递归【猴子吃桃、斐波那契数列、汉诺塔、非规律化递归问题(文件搜索、啤酒问题)】
递归:方法直接或者间接调用自己的形式
- 递归案例导学:使用递归计算1~n的阶乘;
- f(n) = 1*2*3*4*5*...*n ;
- f(n) = f(n-1) * f(n);
1 public class RecursionDemo2 {
2 public static void main(String[] args) {
3 System.out.println(mul(5));
4 }
5 public static int mul(int n){
6 if(n == 1){
7 return 1;
8 }else {
9 return mul(n-1) * n;
10 }
11 }
12 }
示例代码运行结果:
- 同理:计算1~n的和方法思路也是类似的,只不过是将上边代码的乘号变成了加号
1 public class RecursionDemo3 {
2 public static void main(String[] args) {
3 System.out.println(add(5));
4 }
5 public static int add(int n){
6 if(n == 1){
7 return 1;
8 }else {
9 return add(n-1) + n;
10 }
11 }
12 }
示例代码运行结果:
- 递归的经典问题
- 一、
- 猴子吃桃问题:猴子第一天摘下若干个桃子,当即吃了一半,觉得好不过瘾,于是又多吃了一个,第二天又吃了前天剩余桃子数量的一半,觉得好不过瘾,于是又吃了一个,以后每天都是吃前天剩余桃子数量的一半,觉得好不过瘾,又多吃了一个,等到第十天的时候发现桃子只有一个了。问题:请问猴子第一天吃了多少个桃子?
1 public class MonkeyEatPeach {
2 public static void main(String[] args) {
3 /**
4 * 分析:设天数为n,f(n)为第n天剩余的桃子数
5 * n:天数, f(n):剩余桃子数
6 * n = 10 ---> f(n) = 1 = [f(9) - 1] / 2
7 * n = 9 ----> f(9) = [f(8) - 1] / 2
8 * n = 8 ---> f(8) = [f(7) - 1] / 2
9 * ...
10 * n = 2 ---> f(2) = [f(1) / 2 ] - 1
11 * 得出规律:
12 * f(n) = [f(n-1) / 2] - 1
13 * 将 n 替换为 n+1:
14 * f(n+1) = [f(n) / 2] - 1
15 * 得出公式:
16 * 2f(n+1) = f(n) - 2;
17 */
18 System.out.println(f(1));
19 for (int i = 10; i >= 1; i--) {
20 System.out.println("第" + i + "天剩余桃子数量:" + f(i));
21 }
22 }
23 public static int f(int n){
24 if (n == 10){
25 return 1;
26 }else {
27 return 2*f(n + 1) + 2;
28 }
29 }
30 }
示例代码运行结果:
- 二、
- 斐波那契数列:
- 斐波那契数列指的是这样一个数列:1,1,2,3,5,8,13,21,34,55,89...,这个数列从第3项开始,每一项都等于前两项之和,则第n项的数为多少?
1 public class FibonacciDemo {
2 public static void main(String[] args) {
3 for (int i = 1; i <= 11; i++) {
4 System.out.println("第" + i + "项:" + fibonacci(i));
5 }
6 }
7 //1,1,2,3,5,8,13,21,34,55,89...,这个数列从第3项开始,每一项都等于前两项之和
8 public static int fibonacci(int n){
9 if (n == 1 || n == 2){
10 return 1;
11 }
12 else {
13 //化大问题为小问题
14 //第n项等与前两项之和
15 return fibonacci(n-2) + fibonacci(n-1);
16 }
17 }
18 }
示例代码运行结果:
- 三、
- 汉诺塔问题
我们可以先列举出来当盘子数量较少时的移动方案:
- 当A杆有1个盘子时,A -> C
- 当A杆有2个盘子时,A -> B, A -> C, B -> C
- 当A杆有3个盘子时,A -> C, A -> B, C -> B,A->C, B->A, B->C, A->C
1 public class HanoiDemo {
2 public static void main(String[] args) {
3 hanoi(1,'A','B','C');
4 System.out.println();
5 hanoi(2,'A','B','C');
6 System.out.println();
7 hanoi(3,'A','B','C');
8 }
9
10 public static void move(char A, char C){
11 System.out.print(A + "->" + C + " ");
12 }
13
14 /**
15 *
16 * @param n 盘子数量
17 * @param A 盘子的起始位置
18 * @param B 盘子的中转位置
19 * @param C 盘子的目标位置
20 */
21 public static void hanoi(int n,char A, char B, char C){
22 if (n == 1){
23 //只有一个盘子时直接把A柱子上的盘子移动到目的柱子C柱上
24 System.out.print(A + "->" + C + " ");
25 }
26 else {
27 //化大为小,相当于将A柱上n-1个盘子经过C中转后放置在B柱上
28 hanoi(n-1,A,C,B);
29 //将A柱上剩余的那个最大的盘子从A柱移动到C柱
30 move(A,C);
31 //最后将B柱上的n-1个盘子经A中转后全部放置在C盘上边
32 hanoi(n-1,B,A, C);
33 }
34 }
35 }
示例代码运行结果:
可以看到,运行结果与我们之前手动推敲的如出一辙~
参考B站的视频:我感觉这个是讲的挺清楚的,分享给大家哈~
- 非规律化递归问题:文件搜索
- 需求:从D盘中,搜索出某个文件名称并输出绝对路径;
- 分析:
- 先定位出一级文件对象;
- 遍历全部的一级文件对象,判断是否是文件;
- 如果是文件,判断是否是自己想要的;
- 如果是文件夹,需要继续以上3个步骤(递归)。
1 public class RecursionDemo5 {
2 public static void main(String[] args) {
3 searchFile(new File("D:/"), "XMind.exe");
4 }
5
6 /**
7 * @param dir 文件对象,被搜索的文件目录
8 * @param fileName 被搜索的文件名
9 */
10 public static void searchFile(File dir, String fileName) {
11 if (dir != null && dir.length() > 0){ //过滤掉空的文件夹和空的文件
12 //提取当前目录下的一级文件对象
13 File[] files = dir.listFiles();
14 if (files != null){
15 for (File file : files) {
16 if (file.isFile()){
17 if (file.getName().contains(fileName)){
18 System.out.println(file.getAbsolutePath());
19 }
20 }
21 else {
22 searchFile(file,fileName);
23 }
24 }
25 }
26 }
27 }
28 }
示例代码运行结果:
- 啤酒问题:啤酒2元1瓶,4个盖子可以换一瓶,2个空瓶可以换一瓶,请问:10元钱可以和多少瓶啤酒,剩余多少空瓶和盖子?
- 答案:15瓶 3盖子 1瓶子
1 public class BearDemo {
2 public static int totalNumber;
3 public static int lastEmptyBottleNumber;
4 public static int lastCoverNumber;
5 public static void main(String[] args) {
6 buyBear(10);
7 System.out.println(totalNumber);
8 System.out.println(lastEmptyBottleNumber);
9 System.out.println(lastCoverNumber);
10 }
11 public static void buyBear(int money){
12 int buyNumber = money / 2;
13 totalNumber += buyNumber;
14 //计算每一轮此的瓶子数量
15 int bottleNumber = lastEmptyBottleNumber + buyNumber;
16 //计算每一轮此盖子的数量
17 int coverNumber = lastCoverNumber + buyNumber;
18 //将瓶子换算成钱
19 int allMoney = 0;
20 if (bottleNumber >= 2){
21 //这么写是为了处理瓶子数量为单数的情况,所以此处不能写为 allMoney += bottleNumber
22 allMoney += (bottleNumber / 2) * 2;
23 }
24 //换算不完的放到 lastEmptyBottleNumber 中
25 lastEmptyBottleNumber = bottleNumber % 2;
26 //同理,盖子的换算步骤也是类似的
27 if (coverNumber >= 4){
28 allMoney += (coverNumber / 4) * 2;
29 }
30 lastCoverNumber = coverNumber % 4;
31 //递归的条件为钱数大于2
32 if (allMoney >= 2){
33 buyBear(allMoney);
34 }
35 }
36 }
程序运行结果: