【重拾Java系列】—— 对于递归的理解

概述:本篇博客中并未涉及一些关于递归的基础概念与语法,而是通过几个例子来理解递归的思想与应用。


一、简述传参机制

1.一个小知识点
在Java中的传参机制分为两种。

对于基本数据类型,传递的是值,形参的改变不会对实参产生影响。
对于引用数据类型,传递的是地址,通过形参可以改变实参。

2.在main方法中调用方法时,会在栈中生成一个新的方法栈。
当方法调用结束后,就会返回。
// 写一个递归的方法来理解

public void test(int n){
	if(n > 2){
		test(n - 1);
	}
	System.out.println("n = " + n);
}

在这里也有一个坑,如果不注意或者对于递归调用理解的不透彻就会掉坑。
掉坑想法:
假设传入的参数 n 为4,n > 2,进入递归调用,此时 n = 3, n > 2,再次进入递归调用 n = 2, 此时不满足再进行递归调用的条件了,所以输出 n 的值 n = 2。程序结束。

为了更好的辅助理解,我在下面画一个JVM的内存图。
![](https://img-blog.csdnimg.cn/fe5cfb7d0fa24becae1442eb241394f2.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAUmVTdGFydEBYaWFvWmhhbw==,size_20,color_FFFFFF,t_7

  • 忽略画的很丑(doge)
  • 在每次递归调用的时候,都会生成一个新的方法栈。
  • 但是栈执行后就会返回,但是仍然是执行整个调用的方法
  • 在n = 2时,递归结束,输出 n = 2
    但是程序并没有直接结束,而是开始返回。
    所以返回到上一层,n = 3,也就导致了仍然会输出n =3
    最后输出 n = 4后,程序才会完全结束
  • 返回后,没用的方法栈就会自动销毁
二、递归的几个经典问题

概述: 都是通过创建一个单独了类来保存算法


1. 斐波那契数列

此处实现输出第n项的斐波那契数

import java.util.Scanner;
/**
 * 斐波那契数列: 1、1、2、3、5、8 ......
 */
public class Fibonacci {
    public static void main(String[] args){

        Scanner sc = new Scanner(System.in);

        //输出斐波那契的前n项
        System.out.println("请输入想要输出斐波那契数的第n项的n:");
        int n = sc.nextInt();

        //对FF类实例化
        FF f = new FF();

        System.out.println("第" + n + "项斐波那契数为:" + f.print(n));
    }
}

//定义一个类,完成斐波那契数列的具体算法
class FF{

    public int print(int n){

        //对于数列的前两项进行特殊处理
        if (n == 1 || n == 2){
            return 1;
        }else if(n > 2){
            return (print(n - 1) + print(n - 2));
        }else{
            System.out.println("error!");
            return -1;
        }
    }
}

  • 对于斐波那契数列很好理解,所以不进行进一步的说明啦.
2. 猴子吃桃问题
  • 猴子吃桃子问题,从第一天开始,每天吃掉剩余量的一半加一个桃子,当到第十天没吃之前仅剩一个桃子
  • 探索第 n 天 还剩多少桃子(吃之前的剩余量)

  • 第十天: 1个桃子
  • 第九天: (1 + 1) * 2 = 4 个桃子
  • 第八天: (4 + 1) * 2 = 10 个桃子

  • 可以发现:
  • (1)如果为第十天,那么改天桃子的剩余量为 1
  • (2)如果为第 n 天,那么改天桃子的剩余量为 (第n-1天桃子的剩余量 + 1) * 2
  • (3)对于第十天剩余的桃子数已经不满足吃一半加一个的条件了,所以超过10天提示报错
/**
 * 对于递归问题的关键:
 *  (1)递归问题有一个通项公式和一/多个特殊情况
 *  (2)要不断贴近特殊情况,类似于退出循环
 */
public class PeachNum {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入第几天:");
        int day = sc.nextInt();

        //为PP类创建对象
        PP p = new PP();
        int res = p.peach(day);
        if(res != -1){
            System.out.println("第" + day + "天桃子的剩余量为: " + res);
        }

    }
}

class PP{
    /*创建一个方法时的思路
    (1)对于方法要返回什么数据类型或空
    (2)方法名 与 访问权限
    (3)参数列表
    (4)方法体也就是要完成的具体的功能
     */

    public int peach(int day){
        //第十天桃子的剩余量为1个
        if(day == 10){
            return 1;
        }else if(day >= 1 && day < 10){
            return ((peach(day + 1) + 1) * 2);
        }else{
            System.out.println("error!");
            return -1;
        }
    }
}
3. 老鼠走迷宫问题
  • 迷宫问题:
  • 从起点成功走到出口视为问题解决成功的情况
public class Maze {
    public static void main(String[] args) {

        /*创建一个二维矩阵代表迷宫
        此处选择六乘六矩阵,四周都是墙
        起点在 (1, 1) 终点在(4, 4)
        在迷宫里设置两个障碍物(2, 1)、(2, 2)
         */
        int [][] map = new int[6][6];

        //设置四周的墙
        for (int i = 0; i < 6; i++) {
            map[0][i] = 1;
            map[5][i] = 1;
            map[i][0] = 1;
            map[i][5] = 1;
        }

        //设置特殊障碍点
        map[2][1] = 1;
        map[2][2] = 1;

        //调用MM方法开始
        MM m = new MM();
        m.remove(map, 1, 1);

        //判断是否成功到达出口
        if(map[4][4] == 2){
            System.out.println("小老鼠已经成功的走出了迷宫!");
        }else {
            System.out.println("小老鼠迷路了");
        }
        //打印当前的迷宫
        System.out.println("===老鼠走迷宫的轨迹如下: ");
        for (int k = 0; k < map.length; k++) {
            for (int j = 0; j < map[k].length; j++) {
                System.out.print(map[k][j] + " ");
            }
            System.out.println();
        }
    }
}

class MM{
/*
对于表示的设定:
(1) 0 代表可以走的路、 1 代表墙、 2 代表已经走过的路、 3代表不能通过的路
(2) 所以老鼠成功走出迷宫的问题就变成了 map[5][5] == 3 的判断问题

对于方向选择的设定:
对于本个迷宫我们已经知道了起点在左上角,终点在右上角
所以我选择方向的顺序为: 下 -> 右 -> 左 -> 上

对于当前位置是否为通路的判定方法:
当前位置的 上/下/左/右 有可以移动的位置
*/

    public boolean remove(int [][] map, int i, int j){
        //此处参数列表我选择了三个 map代表迷宫的地图 (i, j) 代表当前的位置
        if(map[4][4] == 2){
            return  true;
        }else{
            if(map[i][j] == 0){
                //默认当前的位置是通路
                map[i][j] = 2;
                if(remove(map, (i + 1), j)){
                    return true;

                }else if(remove(map, i, j + 1)){
                    return true;
                }else if(remove(map, i, j - 1)){
                    return true;
                }else if(remove(map, i - 1, j)){
                    return true;
                }else{
                    map[i][j] = 3;
                    return false;
                }
            }else{
                //map[i][j] = 1, 2, 3
                return false;
            }

        }

    }
}
  • 对于大部分注释我直接写到了代码里
  • 主要分为两个部分:在主方法中利用二维数组创建一个迷宫,在MM类中去实现如何来走迷宫的算法
4. 汉诺塔问题
5. 九皇后问题
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bow.贾斯汀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值