Java programming: recursion (maze problem, eight queen problem)

Recursive application scenarios

Look at a practical application scenario, maze problem (backtracking), recursion (Recursion)Insert picture description here

The concept of recursion

Simply put: Recursion is a method 自己调用自己, and different variables are passed in each call. Recursion helps programmers solve complex problems and can make the code simple

Recursive call mechanism

  1. Printing problem
  2. Factorial problem
  3. Illustrated instructions (for example, printing problems)
    Insert picture description here
  4. Code demo
package recursion;

public class RecursionTest {
    
    
    public static void main(String[] args) {
    
    
        //test(10);
        System.out.println(factorial(10));;
    }
    // 打印问题
    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 factorial(n - 1) * n;
        }
    }
}

What kind of problems can recursion solve

  1. Various mathematical problems such as: 8 Queens Problem, Tower of Hanoi, Factorial Problem, Maze Problem, Ball and Basket Problem (Google Programming Contest)
  2. Recursion is also used in various algorithms, such as fast sorting, merge sort, binary search, divide and conquer algorithm, etc.
  3. Problems that will be solved with the stack -> The first return code is more concise

Important rules for recursion

  • When a method is executed, a new protected independent space (stack space) is created
  • The local variables of the method are independent and will not affect each other, such as n variables
  • If the method uses a reference type variable (such as an array), the data of the reference type will be shared.
  • The recursion must approach the condition of exiting the recursion, otherwise it will be infinite recursion, a StackOverflowError will appear, and it will be a dead turtle :)
  • When a method is executed, or when it encounters a return, it will return, and whoever calls it will return the result to whoever. At the same time, when the method is executed or returned, the method is also executed.

Recursive maze problem

Insert picture description here

Description:

  1. The path obtained by the ball is related to the path finding strategy set by the programmer, that is: the order of up, down, left, and right to find the way is related
  2. When you get the path of the ball, you can first use (bottom right, top left), and then change to (top right, bottom left) to see if the path has changed.
    Test backtracking
  3. Thinking: How to find the shortest path?

Code

package recursion;

public class MiGong {
    
    
    public static void main(String[] args) {
    
    
        // 先创建一个二维数组,模拟迷宫
        // 地图
        int[][] map = new int[8][7];
        // 使用1表示墙
        // 先把上下置为1
        for (int i = 0; i < 7; i++) {
    
    
            map[0][i] = 1;
            map[7][i] = 1;
        }
        // 再把左右置为1
        for (int i = 0; i < 8; i++) {
    
    
            map[i][0] = 1;
            map[i][6] = 1;
        }
        map[3][1] = 1;
        map[3][2] = 1;
        // 输出地图
        System.out.println ("当前地图情况为:");
        for (int i = 0; i < 8; i++) {
    
    
            for (int j = 0; j < 7; j++) {
    
    
                System.out.print(map[i][j] + " ");
            }
            System.out.println();
        }
        // 使用递归回溯给小球找路
        setWay2(map, 1, 1);
        // 输出新的地图,小球走过,并标识过的地图
        System.out.println("小球走过,并标识过的地图:");
        for (int i = 0; i < 8; i++) {
    
    
            for (int j = 0; j < 7; j++) {
    
    
                System.out.print(map[i][j] + " ");
            }
            System.out.println();
        }
    }

    // 使用递归回溯来给小球招录

    /**
     * 说明:
     * 1. map表示地图
     * 2. i,j 表示出发点(1,1)
     * 3. 如果小球能到达(6,5),则说明通路找到
     * 4. 约定:
     * 当map[i][j] = 0 时,表示该点还没有走过
     * 当map[i][j] = 1 时,表示该点为墙
     * 当map[i][j] = 2 时,表示该点为通路,可以走
     * 当map[i][j] = 3 时,表示该点已经走过,但是不通
     * 5. 在走迷宫时,需要确定一个策略(方法),下 → 右 → 上 → 左,如果该点走不通,再回溯
     *
     * @param map 表示地图
     * @param i   从哪个位置开始找
     * @param j   从哪个位置开始找
     * @return 找到返回真,没找到返回假
     */
    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;  // 假定该点可以走通
                if (setWay(map, i + 1, j)) {
    
      // 向下走
                    return true;
                } else if (setWay(map, i, j + 1)) {
    
        // 向右走
                    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;
                }
            } else {
    
      // 如果map[i][j] != 0,可能是1,2,3
                return false;
            }
        }
    }

    // 修改找路的策略 上 → 右 → 下 → 左
    public static boolean setWay2(int[][] map, int i, int j) {
    
    
        if (map[6][5] == 2) {
    
     // 通路已经找到
            return true;
        } else {
    
    
            if (map[i][j] == 0) {
    
       // 如果当前这个点还没有走过
                // 按照策略 '上 → 右 → 下 → 左' 走
                map[i][j] = 2;  // 假定该点可以走通
                if (setWay2(map, i - 1, j)) {
    
      // 向上走
                    return true;
                } else if (setWay2(map, i, j + 1)) {
    
        // 向右走
                    return true;
                } else if (setWay2(map, i + 1, j)) {
    
        // 向下走
                    return true;
                } else if (setWay2(map, i, j - 1)) {
    
    
                    return true;
                } else {
    
    
                    // 说明该点走不通,是思路
                    map[i][j] = 3;
                    return false;
                }
            } else {
    
      // 如果map[i][j] != 0,可能是1,2,3
                return false;
            }
        }
    }
}

Recursion-Eight Queens Problem (Backtracking Algorithm)

Introduction to the Eight Queens Problem

The eight queens problem is an old and famous problem, a typical case of backtracking algorithms. The problem was raised by the chess player Max Bethel in 1848: place eight queens on an 8×8 chess board so that they cannot attack each other, that is: no two queens can be in the same row , In the same column or on the same diagonal line, ask how many ways there are. (92 types)
Insert picture description here
Insert picture description here

Analysis of the Algorithms of the Eight Queens Problem

  1. Put the first queen in the first row and first column

  2. Put the second queen in the first column of the second row, and then judge whether it is OK. If it is not OK, continue to put it in the second and third columns, put all the columns in order, and find a suitable one

  3. Continue to the third queen, still the first column, the second column... Until the eighth queen can be placed in a non-conflicting position, it is considered to have found a correct solution

  4. When a correct solution is obtained, when the stack rolls back to the previous stack, it will start to backtrack, that is, all correct solutions placed in the first column of the first queen will be obtained.

  5. Then go back and continue the first queen to put the second column, and then continue to cycle through the steps 1, 2, 3, 4

  6. 【Schematic】
    Insert picture description here

  7. Explanation : In theory, a two-dimensional array should be created to represent the chessboard, but in fact, a one-dimensional array can be used to solve the problem through an algorithm. arr[8] = {0, 4, 7, 5, 2, 6, 1 , 3} //corresponding to arr subscript indicates the row, that is, the queen, arr[i] = val, val indicates the i+1 queen, placed in the val+1 column of the i+1 row

Code

package recursion;

public class Queue8 {
    
    
    // 定义一个max共有多少个皇后
    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.printf("一共有%d种解法",count);
    }

    // 查看当我们放置第n个皇后,就去监测该皇后是否和前面已经摆放的皇后冲突

    // 编写一个方法,放置第n个皇后
    // 特别注意:check是每一次递归时,进入到check中都有一次for循环,因此会有回溯
    private void check(int n){
    
    
        if(n == max){
    
       // n=8 其实前八个皇后已经放好
            printQueue();
            return ;
        }

        // 依次放入皇后,并判断是否冲突
        for (int i = 0; i < max; i++) {
    
    
            // 先把当前皇后放到该行第1列
            array[n] = i;
            // 判断当放置第n个皇后到i列是,是否冲突
            if(judge(n)){
    
       // 不冲突
                check(n+1);
            }
            // 如果冲突,继续执行array[n] = i;及将第n个皇后,放置在本行的后移的一个位置
        }
    }

    /**
     * @param n 表示第n个皇后
     * @return 返回是否冲突
     */
    private boolean judge(int n) {
    
    
        for (int i = 0; i < n; i++) {
    
    
            // 说明
            // 1. array[i] == array[n] 表示判断第n个皇后是否和第i个皇后处于同一列
            // 2. Math.abs(n - i) == Math.abs(array[n] - array[i]) 表示表示判断第n个皇后是否和第i个皇后处于同一斜线
            // 3. 是否在同一行,没有必要判断,因为n代表行,每次递增
            if (array[i] == array[n] || Math.abs(n - i) == Math.abs(array[n] - array[i])) {
    
    
                return false;
            }
        }
        return true;
    }

    // 写一个方法,可以将皇后摆放的位置输出
    private void printQueue() {
    
    
        count++;
        for (int j : array) {
    
    
            System.out.print(j + " ");
        }
        System.out.println();
    }


}

Guess you like

Origin blog.csdn.net/KaiSarH/article/details/108745116