JAVA数据结构 07.递归 迷宫问题 八皇后问题

JAVA数据结构 07.递归 迷宫问题 八皇后问题

1. 递归介绍和解决思路

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

​ 递归有助于编程者解决复杂的问题(只需要关注每一次递归解决的问题),同时可以让代码变得简洁.

递归调用规则:

  • 当程序执行到一个方法时,就会开辟一个独立的空间(存储再栈中);
  • 每个空间的数据(局部变量)都是独立的;
  • 递归必须向退出递归的条件逼近,否则就是无限递归,产生栈溢出错误
  • 当一个方法执行完毕,或者遇到return时,就会返回到调用该方法的地方继续执行下面的程序.

递归解决问题的思路: 注重三个方面

  • 找到整个递归的终止条件:递归应该在什么时候结束?
  • 找到返回值:应该给上一级递归返回什么信息?
  • 找单次递归的任务:本次递归应该做什么?

2. 递归案例:

递归学习思路以及更多案例

2.1 迷宫回溯问题

场景:设置迷宫的墙壁和出口入口,寻找逃出迷宫的路径

在这里插入图片描述

说明:

/**
* 说明:
*  1. map 表示地图
*  2. i,j 表示从地图的哪个位置开始出发,起始位置(1,1)
*  3. 如果小球能到map[6][5]位置,则说明通路找到
*  4. 当map[i][j] 为0 表示当前位置还没有走过;为1 表示墙;为2 表示通路可以走并且已经走过;为3 表示该点走过并且上下左右四个位置都走不通
*  5. 策略: 方向判断顺序: 下->右->上->左, 如果该点四个位置都走不通,则回溯到上一个点(递归)
*/

思路分析:

  • 找到整个递归的终止条件: 小球到达出口位置[6][5] || 递归回溯到初始点发现走不通
  • 找返回值,应该给上一层返回什么信息? 返回该点i,j 是否能走通 boolean
  • 找到单次递归的任务: 需要按照策略顺序判断四个方向是否可以走通,可以返回true,不可以返回false

代码实现:

public class MiGong {
    
    
    public  static void main(String args[]){
    
    
        int[][] map = new int[8][7];
        //初始化迷宫
        setMap(map);
        showMap(map);
        //寻找通路
        setWay(map,1,1);
        showMap(map);
    }
    public static void setMap(int[][] map){
    
    
        //上下两行
        for (int i = 0; i <map[i].length ; i++) {
    
    
            map[0][i] = 1;
            map[7][i] = 1;
        }
        //左右两列
        for (int i = 0; i <map.length ; i++) {
    
    
            map[i][0] = 1;
            map[i][6] = 1;
        }
        //挡板
        map[3][1] = 1;
        map[3][2] = 1;
    }

    public static void showMap(int[][] map){
    
    
        System.out.println("此时的地图情况:");
        for (int[] i:map) {
    
    
            for (int data: i) {
    
    
                System.out.print(data+" ");
            }
            System.out.println();
        }
    }

    /**
     * 说明:
     *  1. map 表示地图
     *  2. i,j 表示从地图的哪个位置开始出发,起始位置(1,1)
     *  3. 如果小球能到map[6][5]位置,则说明通路找到
     *  4. 当map[i][j] 为0 表示当前位置还没有走过;为1 表示墙;为2 表示通路可以走并且已经走过;为3 表示该点走过并且上下左右四个位置都走不通
     *  5. 策略: 方向判断顺序: 下->右->上->左, 如果该点四个位置都走不通,则回溯到上一个点(递归)
     * @param map
     * @param i
     * @param j
     */
    /**
     * 递归解决问题的思路:
     *  1. 找到整个递归的终止条件: 小球到达出口位置[6][5] || 递归回溯到初始点发现走不通
     *  2. 找返回值,应该给上一层返回什么信息? 返回该点i,j 是否能走通  boolean
     *  3. 找到单次递归的任务: 需要按照策略顺序判断四个方向是否可以走通,可以返回true,不可以返回false
     */
    public static boolean setWay(int[][] map ,int i,int j){
    
    
        if(map[6][5] == 2){
    
     //通路已经找到,递归的终止条件
            return true;
        }else{
    
    
            if(map[i][j] == 0){
    
     //表示该点还没有走过
                map[i][j] = 2;  //假定该点是可以走通的,如果无法走通,则回溯时将值改为3
                if(setWay(map,i+1,j)){
    
     //向下走
                    return true;
                }else if(setWay(map,i,j+1)){
    
     //向右走   //elseif 表示 向上走失败后 再向右走
                    return true;
                }else if(setWay(map,i-1,j)){
    
     //向上走
                    return true;
                }else if(setWay(map,i,j-1)){
    
     //向左走
                    return true;
                }else{
    
    
                    map[i][j] = 3;
                    return false;   //四个方向都走不通,就判断走不通 将该点值变为3
                }
            }else{
    
    
                // map = 1 2 3 时均不走
                return false;
            }
        }
    }
}

结果:

此时的地图情况:
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 
此时的地图情况:
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 

2.2 八皇后问题

场景:在8*8的国际象棋棋盘上摆放8个皇后,使其不能互相攻击:即任意两个皇后都不能处于同一行,同一列或同一斜线上,问有多少种摆放.

思路分析:

  1. 第一个皇后放在第一行第一列;
  2. 第二个皇后放在第二行第一列,判断是否ok,如果不符合规则,继续放在第二列/第三列…,依次把所有列都放完,找到一个合适的
  3. 继续第三个皇后,直到第八个皇后也能放在不冲突的位置,算找到了一个正确解
  4. 如果一个棋子将8个位置全部尝试后仍然没有正确解,则前面的几个棋子出错,需要回溯进行修改; (回溯重点)
  5. 当得到一个正确解时,回退到上一个栈,开始回溯,即将第一个皇后,放到第一行第一列的所有正确解全部得到;
  6. 1 2 3 4 5 步骤结束后,皇后放在第一行第一列的所有解得到,将第一个皇后放在第一行第二列,继续循环12345步骤,得到所有解.

说明:

  • 将得到的结果用一维数组进行表示,如:arr=[0,4,7,5,2,6,1,3]; 对应arr下标表示第几行,arr的值表示第几列

代码实现:

注意点:如果一个棋子n将8个位置全部尝试后仍然没有正确解,则回回溯到前一个棋子n-1,将其往后移动一位(array[n-1] = i;),再遍历判断n(从第一列开始)

public class Queue8 {
    
    
    //定义皇后的个数
    int max = 8;
    //定义数组array,保存皇后防止位置的结果,比如arr=[0,4,7,5,2,6,1,3];
    int[] array = new int[max];
    static int count = 0;
    public  static void main(String args[]){
    
    
        Queue8 queue8 = new Queue8();
        queue8.check(0);
        System.out.println("共找到多少种解法:"+count);
    }

    /**
     * 放置第n个皇后
     * 递归解决问题的思路:
     *  1. 找到整个递归的终止条件: 当防止第8个皇后结束以后,即n=8时
     *  2. 找到单次递归的任务: 一次递归需要将这个皇后应该放的第n行的 8个列从左到右遍历一遍,如果找到不与前n个皇后冲突的格子,则放入皇后,接下来继续防止n+1个皇后
     *
     *  注意: 如果一个棋子n将8个位置全部尝试后仍然没有正确解,则回回溯到前一个棋子n-1,将其往后移动一位(array[n-1] = i;),再遍历判断n(从第一列开始)
     * @param n
     */
    public void check(int n){
    
    
        if(n == 8){
    
    
            showArray();
            return;
        }

        //依次放入皇后,并判断是否冲突
        for (int i = 0; i <max ; i++) {
    
    
            //先把当前这个皇后放在第n行的第一列
            array[n] = i;
            if(judge(n)){
    
    
                //不冲突,就防止下一个皇后,直到第八个放完
                check(n+1);
            }
            //如果冲突了,继续遍历i++, array[n] = i++;
        }

    }

    /**
     * 需要一个判断棋子是否冲突的方法
     * 判断参数n皇后是否与已经放置的前n-1个皇后冲突
     *     冲突规则:
     *      1.同一列:array[i] == array[n]
     *      2.同一斜线: Math.abs(n-i) == Math.abs(array[n] - array[i])  两个棋子的行距离等于列距离
     * @param n
     * @return
     */
    public boolean judge(int n){
    
    
        for (int i = 0; i <n ; i++) {
    
    
            if(array[i] == array[n] || Math.abs(n-i) == Math.abs(array[n] - array[i])){
    
    
                return false;
            }
        }
        return true;
    }

    //打印一次结果
    public void showArray(){
    
    
        for (int i = 0; i <8 ; i++) {
    
    
            System.out.print(array[i]+" ");
        }
        System.out.println();
        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

猜你喜欢

转载自blog.csdn.net/weixin_44634197/article/details/108561844