一、方法递归的概述
1、什么是方法递归
方法直接调用自己或者间接调用自己的形式称为方法递归( recursion)。
递归做为一种算法在程序设计语言中广泛应用。
2、递归的形式
直接递归:方法自己调用自己。
间接递归:方法调用其他方法,其他方法又回调方法自己。
3、递归存在的问题
递归如果没有控制好终止,会出现递归死循环,导致栈内存溢出现象。
代码演示:(自己调自己)
public static void main(String[] args) {
test();
}
private static void test() {
System.out.println("===============test被执行=================");
test1();//方法递归,直接递归形式 (死循环,最后导致崩溃)
}
private static void test1() {
System.out.println("===============test1被执行=================");
test2();//方法递归,直接递归形式 (死循环,最后导致崩溃)
}
private static void test2() {
System.out.println("===============test2被执行=================");
test();//方法递归,直接递归形式 (死循环,最后导致崩溃)
//===============test1被执行=================
//===============test2被执行=================
//===============test被执行=================
}
二、递归的算法流程和核心要素
1、案例:计算1-n的阶乘
需求:计算1-n的阶乘的结果,使用递归思想解决,我们先从数学思维上理解递归的流程和核心点。
分析 :假如我们认为存在一个公式是 f(n) = 1*2*3*4*5*6*7*…(n-1)*n;
那么公式等价形式就是: f(n) = f(n-1) *n
如果求的是 1-5的阶乘 的结果,我们手工应该应该如何应用上述公式计算。
递归解决问题的思路: 把一个复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。
递归算法三要素大体可以总结为:
递归的公式: f(n) = f(n-1) * n;
递归的终结点:f(1)
递归的方向必须走向终结点;
public static void main(String[] args) {
System.out.println(f(5));//120
}
private static int f(int n) {
if(n == 1){
return n;
}else{
return f(n - 1)*n;
}
}
2、案例:计算1-n的和
需求:
计算1-n的和的结果,使用递归思想解决,我们先从数学思维上理解递归的流程和核心点。
分析 :
假如我们认为存在一个公式是 f(n) = 1 + 2 + 3 + 4 + 5 + 6 + 7 + …(n-1) + n;
那么公式等价形式就是: f(n) = f(n-1) + n
递归的终结点:f(1) = 1
如果求的是 1-5的和 的结果,应该如何计算。
public static void main(String[] args) {
System.out.println(f(10));//55
}
private static int f(int n) {
if(n == 1){
return 1;
}else{
return f(n-1) + n;
}
}
3、经典案例:猴子吃桃问题
题目:
猴子第一天摘下若干桃子,当即吃了一半,觉得好不过瘾,于是又多吃了一个 第二天又吃了前天剩余桃子数量的一半,觉得好不过瘾,于是又多吃了一个 以后每天都是吃前天剩余桃子数量的一半,觉得好不过瘾,又多吃了一个 等到第10天的时候发现桃子只有1个了。
需求:
请问猴子第一天摘了多少个桃子?
分析:
整体来看,每一天都是做同一个事件,典型的规律化问题,
考虑递归三要素:
递归公式:
递归终结点:
递归方向:
public static void main(String[] args) {
System.out.println(f(1));//1534
System.out.println(f(2));//766
System.out.println(f(3));//382
}
private static int f(int n) {
if(n == 10){
return 1;
}
else{
return 2*f(n + 1) + 2;
}
}
三、非规律化案例的转化
1、案例:文件搜索
需求:
文件搜索、从C:盘中,搜索出某个文件名称并输出绝对路径。
分析:
先定位出的应该是一级文件对象
遍历全部一级文件对象,
判断是否是文件 如果是文件,判断是否是自己想要的
如果是文件夹,需要继续递归进去重复上述过程
public static void main(String[] args) {
//2、传入目录 和 文件名称
searchFile(new File("D:/"),"t0e2622116992e6377c47f6e7d4a18d82.kgtemp");
}
/**
* 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 []
//5、判断是否存在一级文件对象,存在才可以遍历
if(files != null && files.length > 0){
for (File file : files) {
//6、判断当前遍历的一级文件对象是文件还是文件夹(目录)
if(file.isFile()){
//7、是不是咱们要找的,是把其路径输出即可
if(file.getName().contains(fileName)){
System.out.println("找到了" + file.getAbsolutePath());
//启动它!
Runtime r = Runtime.getRuntime();
try {
r.exec(file.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
}else{
//8、是文件夹的话,需要继续递归寻找
searchFile(file,fileName);
}
}
}
}
}else{
System.out.println("对不起,当前搜索的位置不是文件夹");
}
}
2、案例:啤酒问题
需求:啤酒2元1瓶,4个盖子可以换一瓶,2个空瓶可以换一瓶,
请问10元钱可以喝多少瓶酒,剩余多少空瓶和盖子。
答案:15瓶 3盖子 1瓶子
//定义一个静态的成员变量用于存储可以买的数量
public static int totalNumber;//总数量
public static int lastcoverNumber;//记录每次剩余的瓶子数目
public static int lastBottleNumber;// 记录每次剩余的瓶盖数目
public static void main(String[] args) {
//1、拿钱买酒
buy(10);
System.out.println("总数: " + totalNumber);
System.out.println("剩余盖子数: " + lastcoverNumber);
System.out.println("剩余瓶子数: " + lastBottleNumber);
}
private static void buy(int money) {
//2、看可以立马买多少酒
int buyNumber = money /2; // 5
totalNumber += buyNumber;
//3、把瓶盖和盖子换算成钱
//统计本轮总的瓶盖数和瓶子数
int coverNumber = lastcoverNumber + buyNumber;
int BottleNumber = lastBottleNumber + buyNumber;
//统计可以换算的钱
int allMoney = 0;
if(coverNumber >= 4){
allMoney += (coverNumber / 4 ) * 2;
}lastcoverNumber = coverNumber % 4 ;
if(BottleNumber >=2){
allMoney += (BottleNumber / 2) * 2;
}lastBottleNumber = (BottleNumber % 2);
if(allMoney >=2){
buy(allMoney);
}
}