夜深人静写算法【递归】

1. 递归的概念

1.1 文字描述

简单的说: 递归就是方法自己调用自己,每次调用时传入不同的变量。

递归有助于编程者解决复杂的问题,同时可以让代码变得简洁

1.2 图示

就以阶乘举例的图示

diguijiecheng

2. 递归的应用场景

2.1 递归能解决什么样的问题

2.1.1 各种数学问题

8皇后问题 , 汉诺塔, 阶乘问题, 迷宫问题, 球和篮子的问题(google编程大赛)

2.1.2 各种算法

快排,归并排序,二分查找,分治算法等。

2.1.3 解决栈的问

递归归代码比较简洁。【树,图,最小生成树】

2.2 递归需要遵守的重要规则

  1. 执行一个方法时,就创建一个新的受保护的独立空间(栈空间)
  2. 方法的局部变量是独立的,不会相互影响, 比如num变量【num:图示里的变量】
  3. 如果方法中使用的是引用类型变量(比如数组),就会共享该引用类型的数据.【引用类型变量会都放到堆内存里】
  4. 递归必须向退出递归的条件逼近,否则就是无限递归,出现【栈溢出】StackOverflowError,死循环)
  5. 当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕。

3.迷宫回溯

3.1思路分析【图示】

image-20220217022158960

3.2 代码实现

package recursion;

import java.util.Random; 

/**
 * @Author 小源同学
 * @Date 2022 02 17 01 59
 * @Describe 迷宫回溯
 **/
public class MazeBacktracking {
    public static void main(String[] args) {
        int y = 8;
        int x = 7;
        int[][] ints = initMap(y, x);
        System.out.println("初始化迷宫:");
        soutInt(ints);

        System.out.println("迷宫回溯后的路径:");
        findMapPath(ints, 1, 1, x, y);
        soutInt(ints);
    }

    /**
     * 迷宫回溯的核心代码
     * 1. 设置终点为map[y-2][x-2]
     * 2. 使用0:表示未走过;使用1:表示墙体;使用2:表示通路;使用3:表示不可通行
     * 3. 寻路策略:下->右->上->左,如果路不通,回溯
     *
     * @param map 初始化的地图
     * @param i   开始的横坐标比如:map[1][1]
     * @param j   开始的纵坐标比如:map[1][1]
     * @param x   地图的列数
     * @param y   地图的行数
     * @return 找到通路返回true, 如果没有找到就返回false
     */
    private static boolean findMapPath(int[][] map, int i, int j, int x, int y) {
        if (map[y - 2][x - 2] == 2) {//表示已经找到通路,直接返回,如果再回溯,就退出回溯
            return true;
        } else {
            if (map[i][j] == 0) {//如果该点没有走过
                //假设这个点可以走
                map[i][j] = 2;
                //然后再判断这个点是否可以通向其他点
                if (findMapPath(map, i + 1, j, x, y)) {//【根据策略】向下走
                    return true;
                } else if (findMapPath(map, i, j + 1, x, y)) {//【根据策略】向右走
                    return true;
                } else if (findMapPath(map, i - 1, j, x, y)) {//【根据策略】向上走
                    return true;
                } else if (findMapPath(map, i, j - 1, x, y)) {//【根据策略】向左走
                    return true;
                } else {//证明这个点不可以走
                    map[i][j] = 3;
                    return false;
                }
            } else {//ap[i][j] != 0 那么这个点的值可能为 1 2 3,则不再继续
                return false;

            }
        }
    }

    /**
     * 初始化迷宫地图,用二维数组(int[8][7])表示,根据需求创建一个地图
     *
     * @param x 列
     * @param y 行
     * @return 返回初始化的地图
     */
    private static int[][] initMap(int y, int x) {
        //使用1:表示墙体
        //使用2:表示通路
        //使用3:表示不可通行
        int[][] map = new int[y][x];
        //初始化上下的墙体:遍历列【x】
        for (int i = 0; i < x; i++) {
            map[0][i] = 1;
            map[y - 1][i] = 1;
        }
        //初始化左右墙体:遍历行【y】
        for (int j = 0; j < y; j++) {
            map[j][0] = 1;
            map[j][x - 1] = 1;
        }
        //随机创建阻挡
        Random rn = new Random();
        //2到y-1之间的数字
        int randomY = rn.nextInt(y - 2) + 2;
        //添加到迷宫里
        //1到3之间的数字
        //如果要修改的话,根据设定的迷宫行与列进行修改
        map[rn.nextInt(y - 2) + 2][rn.nextInt(x-2) + 1] = 1;
        map[rn.nextInt(y - 2) + 2][rn.nextInt(x-2) + 1] = 1;
        return map;
    }

    //打印输出 int[][]
    private static void soutInt(int[][] ints) {
        for (int[] anInt : ints) {
            for (int i : anInt) {
                System.out.print(i + "   ");
            }
            System.out.println();
        }
    }
}

4.八皇后

八皇后游戏体验地址

4.1 问题介绍

八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。

该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:

在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即:任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。

4.2 思路分析【图示】

image-20220217164956927

4.3 代码实现

package recursion;

/**
 * @Author 小源同学
 * @Date 2022 02 17 22 17
 * @Describe 八皇后问题的解决
 **/
public class Queue8 {
    static int count = 0;//统计解决方法的数量
    int max = 8;//定义棋盘的大小【行列一样】
    int[] array = new int[max]; //保存皇后被放置的位置

    public static void main(String[] args) {
        Queue8 queue8 = new Queue8();
        queue8.check(0);//从0开始,实际上是从第一行第一列开始
        System.out.printf("总共有%d中解法", count);
    }

    //下棋:放置第n[0,1,……]个皇后
    private void check(int n) {
        if (n == max) {//结束递归【回溯】,max个皇后已经放置完毕
            print();//打印结果
            return;
        }
        //没有到我们设置的最大阈值,则继续递归【回溯】:依次放入皇后
        for (int i = 0; i < max; i++) {
            //从第一列开始放置,查找可行的位置
            array[n] = i;
            if (judge(n)) {//判断第n个皇后放在第i的位置是否会冲突【合不合理】
                //不冲突的情况下,继续放置下一个皇后【开始递归】
                check(n + 1);
            }
            //如果冲突就继续执行array[n] = i;就是改边列,依次尝试
        }

    }

    //判断皇后摆放的是否合理
    //当我们摆放了第n个皇后,判断该皇后和前面的(n-1)个皇后的位置是否冲突【任意两个皇后都不能处于同一行、同一列或同一斜线上】
    private boolean judge(int n) {
        for (int i = 0; i < n; i++) {
            /*
             * 说明:
             * (下标):(下标+1)说明是第几个皇后,同时也是第几行
             * (array[下标]):说明某行皇后在哪一列上
             * 1. array[n] == array[i] :判断第n个皇后  与   前(n-1)个皇后是否在同一列
             * 1.1 array[n] 代表的是第n个皇后所在的列,array[i] 代表的是前(n-1)个皇后所在的列 如果相等就说明在同一列上
             * 2. Math.abs(n - i) == Math.abs(array[n] - array[i]) 这个比较不好理解,这个是为了判断是否在对角线上【斜线上】
             * 2.1  假设 n=1,这表示是第2个皇后,前面只有一个,所以i = 0
             * 2.2  Math.abs(n - i) = 1
             * 2.3  Math.abs(array[1] - array[0]) 表示的列数相减 ,即 1-0 = 1  【这个时候画个图就会明白,这里考虑的是不是等腰直角三角形的两个腰】
             */
            if (array[n] == array[i] || Math.abs(n - i) == Math.abs(array[n] - array[i])) {
                return false;
            }
        }
        return true;
    }


    //打印皇后摆放的位置输出
    private void print() {
        count++;
        for (int i : array) {
            System.out.print(array[i] + "  ");
        }
        System.out.println();
    }
}

  • 9
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
树形动态规划(Tree DP)是一种解决树状结构问题的算法思想。它利用了树这种特殊的数据结构的性质进行求解,常用来解决树的最优路径、最大值、最小值等类型的问题。 在夜深人静的时候算法,我通常会采用以下步骤来完成树形dp的实现: 第一步是定义状态。我们首先需要确定问题的状态表示方式。对于树形dp来说,常用的状态表示方式是以节点为单位进行表示。我们可以定义dp[i]表示以节点i为根的子树的某种性质,比如最大路径和、最长路径长度等。 第二步是确定状态转移方程。根据问题的特点,我们需要找到状态之间的关系,从而确定状态转移方程。在树形dp中,转移方程常常与节点的子节点相关联。我们可以通过遍历节点的子节点,利用它们的状态来更新当前节点的状态,从而得到新的状态。 第三步是确定初始条件。在动态规划中,我们需要确定初始状态的值。对于树形dp来说,我们可以选择将叶节点作为初始状态,然后逐步向上更新,最终得到整棵树的最优解。 第四步是确定计算顺序。树形dp的计算通常是从根节点开始,自顶向下逐步计算,直到达到叶节点。因为树形dp的计算过程中需要利用到子节点的状态来更新当前节点的状态,所以必须按照计算顺序进行。 夜深人静时,算法树形dp是相对较复杂的算法,需要仔细思考问题的状态表示方式,转移方程以及初始条件。在实现过程中,可以采用递归的方式进行代码编,或者利用栈等数据结构进行迭代实现。 总的来说,夜深人静算法树形dp需要耐心和细心,经过思考和实践,才能顺利解决树状结构问题。但是,一旦理解并掌握了树形dp的思想和方法,就能够高效地解决各种树形结构问题,提升算法的效率和准确性。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小源同学r

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

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

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

打赏作者

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

抵扣说明:

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

余额充值