递归的形式和特点

什么是方法递归?

方法直接调用自己或者间接调用自己的形式称为方法递归( recursion)。
递归做为一种算法在程序设计语言中广泛应用。

递归的形式

直接递归:方法自己调用自己。
间接递归:方法调用其他方法,其他方法又回调方法自己。

方法递归存在的问题?

递归如果没有控制好终止,会出现递归死循环,导致栈内存溢出现象。

递归的算法流程、核心要素

递归算法三要素大体可以总结为:
递归的公式: f(n) =  f(n-1) * n;
递归的终结点:f(1) 
递归的方向必须走向终结点:

递归的经典问题

猴子第一天摘下若干桃子,当即吃了一半,觉得好不过瘾,于是又多吃了一个
第二天又吃了前天剩余桃子数量的一半,觉得好不过瘾,于是又多吃了一个
以后每天都是吃前天剩余桃子数量的一半,觉得好不过瘾,又多吃了一个
等到第10天的时候发现桃子只有1个了。
需求:请问猴子第一天摘了多少个桃子?
分析:
整体来看,每一天都是做同一个事件,典型的规律化问题,考虑递归三要素:
递归公式: 
递归终结点:
递归方向:

代码实现:

/**
 * 递归的精髓:
 * 递归的公式: f(x) = 2f(x+1) + 2;
 * 递归的终结点:f(10)
 * 递归的方向必须走向终结点:
 */
public class RecursionDemo3 {
    public static void main(String[] args) {
        System.out.println(f(10));
    }
    
    /**
     * 推到公式,最重要的就是公式的提取能力
     * f(x) - f(x)/2 - 1 = f(x+1)===> f(x) = 2f(x+1) + 2
     */
    public static int f(int n){
        if(n == 10){
            return 1;
        }else {
            return 2 * f(n+1) + 2;
        }

    }
}

非规律化递归案例-文件搜索

需求:文件搜索、从D:盘中,搜索出某个文件名称并输出绝对路径。
分析:
先定位出的应该是一级文件对象
遍历全部一级文件对象,判断是否是文件
如果是文件,判断是否是自己想要的
如果是文件夹,需要继续递归进去重复上述过程

代码实现:

import java.io.File;
import java.io.IOException;

public class RecursionDemo4 {
    public static void main(String[] args) {
        // 2、传入目录 和 文件名称
        searchFile(new File("D:/"), "devcpp.exe");
    }

    /**
     * 1、搜索某个目录下的全部文件,找到我们想要的文件
     * @param dir  被搜索的原目录
     * @param fileName  被搜索的文件名称
     */
    public static void searchFile(File dir, String fileName){
        // 3、判断dir是否是目录
        if(dir != null && dir.isDirectory()){
            // 4. 提取当前目录下的一级文件对象
            File[] files = dir.listFiles();  // 存在null,[]现象
            if(files != null && files.length > 0){
                // 5、遍历文件列表
                for (File file : files) {
                    // 6、判断当前遍历的一级文件对象是文件 还是  目录
                    if(file.isFile()){
                        // 7、是不是咱们要找的文件
                        if(file.getName().equals(fileName)){
                            System.out.println("文件查找成功:" + file.getAbsolutePath());
                            // 额外的知识,只能自启动.exe的文件
                            Runtime r = Runtime.getRuntime(); // 静态方法返回Runtime对象
                            try {
                                r.exec(file.getAbsolutePath());
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }else {
                        // 8、如果是文件夹,继续遍历
                        searchFile(file, fileName);
                    }
                }
            }
        }else {
            System.out.println("对不起,当前被搜索的不是文件夹,不支持");
        }
    }
}

非规律化递归案例-啤酒问题

需求:啤酒2元1瓶,4个盖子可以换一瓶,2个空瓶可以换一瓶,
请问10元钱可以喝多少瓶酒,剩余多少空瓶和盖子。
答案:15瓶 3盖子 1瓶子

代码实现:

/**
 * 思想概述:
 * 1、对于递归,自己掌握的不好也可以说成是非常差劲的,要好好总给思路是如何来的。
 * 2、本题设置了三个静态变量解决了累加的问题,这是自己需要学习的地方
 * 3、递归函数的三个特性(关系式、递归的方向、结束条件);
 */
public class RecursionDemo6 {
    public static int totalNumber;  // 总数量
    public static int lastBottleNumber; // 记录每次剩余的瓶子个数
    public static int lastBottleCapNumber;  // 记录每次剩余的盖子个数

    public static void main(String[] args) {
        buy(10);
        System.out.println("总数:" + totalNumber);
        System.out.println("剩余盖子数:" + lastBottleCapNumber);
        System.out.println("剩余瓶子数:" + lastBottleNumber);
    }
    public static void buy(int money){
      int buyNumber = money / 2;
      totalNumber += buyNumber;

      // 当前拥有的瓶子数和瓶盖数
      int currBottleNumber = lastBottleNumber + buyNumber;
      int currBottleCapNumber = lastBottleCapNumber + buyNumber;

      // 把瓶子和瓶盖换钱
      int totalMoney = 0;  // 统计瓶子和瓶盖换的钱数
      if(currBottleNumber >= 2){
          totalMoney += (currBottleNumber / 2) * 2;
      }
      lastBottleNumber = currBottleNumber % 2;

      if(currBottleCapNumber >= 4){
          totalMoney += (currBottleCapNumber / 4) * 2;
      }
      lastBottleCapNumber = currBottleCapNumber % 4;

      if(totalMoney >= 2){
          buy(totalMoney);
      }
    }
}