0
点赞
收藏
分享

微信扫一扫

递归 迷宫 八皇后

止止_8fc8 2022-02-17 阅读 35

递归 迷宫 八皇后

递归

基本概念

递归就是方法自己调用自己,每次调用时传入不同的变量。递归有助于比那撑着解决复杂的问题,同时可以让代码变得简单

调用机制

打印问题思路分析:

在这里插入图片描述
递归调用规则:

  • 当程序执行到一个方法时,就会开辟一个独立的空间 (栈)
  • 每个空间的数据 (局部变量) ,是独立的;引用数据类型变量,是共享的
package com.atguigu.recursion;

/**
 * @ClassName RecursionDemo
 * @Author Jeri
 * @Date 2022-02-17 19:34
 * @Description 递归调用机制
 */
public class RecursionDemo {
    //打印问题
    public static void test(int n){
        if(n > 2){
            test(n - 1);
        }

        System.out.println("n = " + n);
    }

    //阶乘问题
    public static int factorial(int n){
        if(n == 1){
            return 1;
        }else{
            return n * factorial(n-1);
        }
    }

    public static void main(String[] args) {
        //通过打印问题 回顾递归调用机制
        test(4);

        int res = factorial(4);
        System.out.println("res = " + res);
    }
}
n = 2
n = 3
n = 4
res = 24

使用场景

  • 各种数学问题如: 8皇后问题 , 汉诺塔, 阶乘问题, 迷宫问题, 球和篮子的问题(google 编程大赛)
  • 各种算法中也会使用到递归,比如快排,归并排序,二分查找,分治算法等
  • 将用栈解决的问题–>递归代码比较简洁

重要规则

  • 执行一个方法时,就创建一个新的受保护的独立空间(栈空间)
  • 方法的局部变量是独立的,不会相互影响
  • 如果方法中使用的是引用类型变量(比如数组),就会共享该引用类型的数据
  • 递归必须向退出递归的条件逼近,否则就是无限递归,出现 StackOverflowError,死龟了:)
  • 当一个方法执行完毕,或者遇到 return,就会返回,遵守谁调用,就将结果返回给谁,同时当方法执行完毕或者返回时,该方法从栈中除去

迷宫问题

在这里插入图片描述
问题描述: 红色部分为障碍,小球需要从左上角移动到右下角,请给出一条可以移动的路径

**思路分析:**自己写不出来 ,递归回溯不知道应该如何描述

  • 下一个位置寻路和当前位置寻路采用相同的策略,所以使用递归

代码实现:

package com.atguigu.recursion;

/**
 * @ClassName Maze
 * @Author Jeri
 * @Date 2022-02-17 20:00
 * @Description 迷宫
 */
public class Maze {

    public static int[][] maze;
    public static int rows;
    public static int lines;

    /*
     * @Description 设置迷宫信息
     * @Date 2022/2/17 20:59
     * @param rows 迷宫行数
     * @param lines 迷宫列数
     **/
    public static void setMaze(int rows,int lines){
        //创建二维数组 模拟迷宫
        maze = new int[rows][lines];
        //迷宫数字状态 1:障碍 0:初始状态 2:此路通 3:此路不通
        //迷宫设置障碍 上下两行
        for(int i = 0;i < 7;i++){
            maze[0][i] = 1;
            maze[7][i] = 1;
        }
        //左右两列
        for(int i = 0;i < 8;i++){
            maze[i][0] = 1;
            maze[i][6] = 1;
        }
        //特殊点
        maze[3][1] = 1;
        maze[3][2] = 1;



    }
    /*
     * @Description 返回迷宫信息
     * @Date 2022/2/17 20:59
     **/
    public static void getMaze(){
        //输出地图
        System.out.println("迷宫的情况:");
        for(int i = 0;i < rows;i++){
            for(int j = 0;j < lines;j++){
                System.out.printf(maze[i][j] + " ");
            }
            System.out.println();
        }
    }

    /*
     * @Description 寻路策略
     * @Date 2022/2/17 21:01
     * @param i 横坐标
     * @param j 纵坐标
     * maze[i][j] 表示迷宫的出发点
     * 如果能够到达maze[6][5] 则说明通路找到
     * 约定:迷宫数字状态 1:障碍 0:初始状态 2:此路通 3:此路不通
     * 寻路时 需要自定义寻路策略 不同的寻路策略可能得到不同的路径
     * @return maze[i][j]是否可通
     **/
    public static boolean setWay(int i,int j){
        if(maze[6][5] == 2){
            //最后一个节点为通
            return true;
        }else{
            //小球开始从节点 maze[i][j]开始寻路
            if(maze[i][j] == 0){
                //0:初始状态 当前节点还没有走过

                //设置寻路策略 下 右 上 左
                //假设该点可以走通
                maze[i][j] =  2;
                if(setWay(i + 1,j)){//下
                    return true;
                }else if(setWay(i,j + 1)){//右
                    return true;
                }else if(setWay(i - 1 ,j)){//上
                    return true;
                }else if(setWay(i,j - 1)){//左
                    return true;
                }else{
                    //该点是死路
                    maze[i][j] = 3;
                    return false;
                }

            }else{
                // maze[i][j] != 0  1 2 3 都不能过这一点
                return false;
            }
        }
    }
    public static void main(String[] args) {
        //查看地图情况
        rows = 8;
        lines = 7;
        setMaze(rows,lines);
        getMaze();

        //使用递归寻路
        setWay(1,1);

        //获得迷宫信息
        //将所有数字为2的点连起来 即为路径
        System.out.println();
        System.out.println("将所有数字为2的点连起来 即为路径");
        getMaze();
    }
}

迷宫的情况:
1 1 1 1 1 1 1 
1 0 0 0 0 0 1 
1 0 0 0 0 0 1 
1 1 1 0 0 0 1 
1 0 0 0 0 0 1 
1 0 0 0 0 0 1 
1 0 0 0 0 0 1 
1 1 1 1 1 1 1 

将所有数字为2的点连起来 即为路径
迷宫的情况:
1 1 1 1 1 1 1 
1 2 0 0 0 0 1 
1 2 2 2 0 0 1 
1 1 1 2 0 0 1 
1 0 0 2 0 0 1 
1 0 0 2 0 0 1 
1 0 0 2 2 2 1 
1 1 1 1 1 1 1 

八皇后

**问题描述:**八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848 年提出:在 8×8 格的国际象棋上摆放八个皇后,使其不能互相攻击,即:任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法(92)。
在这里插入图片描述

思路分析:

  • 第一个皇后先放第一行第一列
  • 第二个皇后放在第二行第一列、然后判断是否 OK, 如果不 OK,继续放在第二列、第三列、依次把所有列都
    放完,找到一个合适
  • 继续第三个皇后,还是第一列、第二列……直到第 8 个皇后也能放在一个不冲突的位置,算是找到了一个正确
  • 当得到一个正确解时,在栈回退到上一个栈时,就会开始回溯,即将第一个皇后,放到第一列的所有正确解,
    全部得到
  • 然后回头继续第一个皇后放第二列,后面继续循环执行 1,2,3,4 的步骤

**说明:**理论上应该创建一个二维数组来表示棋盘,但是实际上可以通过算法,用一个一维数组即可解决问题. arr[8] =
{0 , 4, 7, 5, 2, 6, 1, 3}

  • arr[i] 下标i 表示第 i 行,即第 i 个皇后,(注:皇后编号从 0-7)
  • arr[i] = value ,value 表示第 i 个皇后放在 第 value 列 (注:皇后摆放位置的列从0-7)

注:实际上采用暴力解法,类似于8重 for 循环来解。 采用递归算法使得代码变得简洁,理解起来及其抽象。递归回溯相当于记录 当前一层 for 循环的检查点 checkpoint ,多debug一下吧

package com.atguigu.recursion;

/**
 * @ClassName Queen8
 * @Author Jeri
 * @Date 2022-02-17 22:15
 * @Description 八皇后问题
 */
public class Queen8 {
    //max 表示皇后数量
    public static int max;
    //arr表示皇后存放的位置
    public static int[] arr;
    //count 表示解法总数
    public static int count = 0;

    private void check(int n){
        if(n == max){
            print();
            return;
        }

        //依次放入皇后 判断是否冲突
        for(int i = 0;i < max;i++){
            //当前皇后 先放在第一列
            arr[n] = i;
            //判断第n个皇后到第i列时 是否冲突
            if(!isConflict(n)){
                //不冲突 放置下一个皇后
                check(n+1);
            }

            //冲突 for 循环继续执行 arr[n] = i
            //当前皇后位置后移

        }
    }
    /*
     * @Description 检测第n个皇后是否与前n-1个皇后冲突
     * @Date 2022/2/17 22:46
     * @param n 第n个皇后
     * @return [n] false 不冲突
     */
    private boolean isConflict(int n){
        for(int i = 0;i < n;i++){
            //皇后冲突原则:任意两个皇后都不能处于同一行、同一列或同一斜线上
            //1.同一行 一维数组表示后 任意两个皇后不能处于用一行
            //2.同一列 arr[i] == arr[n]
            //3.同一斜线  abs(纵坐标 / 横坐标) = 1
            // Math.abs(n-i) == Math.abs(arr[n] - arr[i])
            if(arr[i] == arr[n] || Math.abs(n-i) == Math.abs(arr[n] - arr[i])){
                return true;
            }
        }

        return false;
    }

    /*
     * @Description 输出皇后的摆放的位置
     * @Date 2022/2/17 22:16
     **/
    private void print(){
        count++;
        for(int i = 0; i < arr.length;i++){
            System.out.printf(arr[i] + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        //max 表示皇后数量
        max = 8;
        //arr表示皇后存放的位置
        arr = new int[max];
        
        Queen8 queen8 = new Queen8();
        queen8.check(0);
        System.out.printf("总共有%d种解法\n",count);
    }
}
0 4 7 5 2 6 1 3 
0 5 7 2 6 3 1 4 
0 6 3 5 7 1 4 2 
0 6 4 7 1 3 5 2 
1 3 5 7 2 0 6 4 
1 4 6 0 2 7 5 3 
1 4 6 3 0 7 5 2 
1 5 0 6 3 7 2 4 
1 5 7 2 0 3 6 4 
1 6 2 5 7 4 0 3 
1 6 4 7 0 3 5 2 
1 7 5 0 2 4 6 3 
2 0 6 4 7 1 3 5 
2 4 1 7 0 6 3 5 
2 4 1 7 5 3 6 0 
2 4 6 0 3 1 7 5 
2 4 7 3 0 6 1 5 
2 5 1 4 7 0 6 3 
2 5 1 6 0 3 7 4 
2 5 1 6 4 0 7 3 
2 5 3 0 7 4 6 1 
2 5 3 1 7 4 6 0 
2 5 7 0 3 6 4 1 
2 5 7 0 4 6 1 3 
2 5 7 1 3 0 6 4 
2 6 1 7 4 0 3 5 
2 6 1 7 5 3 0 4 
2 7 3 6 0 5 1 4 
3 0 4 7 1 6 2 5 
3 0 4 7 5 2 6 1 
3 1 4 7 5 0 2 6 
3 1 6 2 5 7 0 4 
3 1 6 2 5 7 4 0 
3 1 6 4 0 7 5 2 
3 1 7 4 6 0 2 5 
3 1 7 5 0 2 4 6 
3 5 0 4 1 7 2 6 
3 5 7 1 6 0 2 4 
3 5 7 2 0 6 4 1 
3 6 0 7 4 1 5 2 
3 6 2 7 1 4 0 5 
3 6 4 1 5 0 2 7 
3 6 4 2 0 5 7 1 
3 7 0 2 5 1 6 4 
3 7 0 4 6 1 5 2 
3 7 4 2 0 6 1 5 
4 0 3 5 7 1 6 2 
4 0 7 3 1 6 2 5 
4 0 7 5 2 6 1 3 
4 1 3 5 7 2 0 6 
4 1 3 6 2 7 5 0 
4 1 5 0 6 3 7 2 
4 1 7 0 3 6 2 5 
4 2 0 5 7 1 3 6 
4 2 0 6 1 7 5 3 
4 2 7 3 6 0 5 1 
4 6 0 2 7 5 3 1 
4 6 0 3 1 7 5 2 
4 6 1 3 7 0 2 5 
4 6 1 5 2 0 3 7 
4 6 1 5 2 0 7 3 
4 6 3 0 2 7 5 1 
4 7 3 0 2 5 1 6 
4 7 3 0 6 1 5 2 
5 0 4 1 7 2 6 3 
5 1 6 0 2 4 7 3 
5 1 6 0 3 7 4 2 
5 2 0 6 4 7 1 3 
5 2 0 7 3 1 6 4 
5 2 0 7 4 1 3 6 
5 2 4 6 0 3 1 7 
5 2 4 7 0 3 1 6 
5 2 6 1 3 7 0 4 
5 2 6 1 7 4 0 3 
5 2 6 3 0 7 1 4 
5 3 0 4 7 1 6 2 
5 3 1 7 4 6 0 2 
5 3 6 0 2 4 1 7 
5 3 6 0 7 1 4 2 
5 7 1 3 0 6 4 2 
6 0 2 7 5 3 1 4 
6 1 3 0 7 4 2 5 
6 1 5 2 0 3 7 4 
6 2 0 5 7 4 1 3 
6 2 7 1 4 0 5 3 
6 3 1 4 7 0 2 5 
6 3 1 7 5 0 2 4 
6 4 2 0 5 7 1 3 
7 1 3 0 6 4 2 5 
7 1 4 2 0 6 3 5 
7 2 0 5 1 4 6 3 
7 3 0 2 5 1 6 4 
总共有92种解法

参考文献

尚硅谷Java数据结构与java算法

举报

相关推荐

0 条评论