Data structure (eight)-recursion

Preface

  The first two articles introduced the basic knowledge of the stack and related applications , mainly introduced the basic knowledge of the stack, the application scenarios of the stack, and the implementation of the stack. Finally, by comparing the difference between the stack and the queue, we can better let everyone Deeply understand the difference and connection between the two. Due to the wide range of applications of the stack, we have separately introduced a typical application of the stack through an article-the concrete realization of the conversion between inverse Polish expressions and infix expressions and postfix expressions, so as to let everyone understand and understand the stack. The application has a deeper understanding. Today, I will introduce you to a more commonly used idea in data structure-recursion. Mainly introduce the related concepts of recursion and further explain the usage of recursion through some cases. Different from stacks and queues, recursion is a kind of thought without a clear definition. First introduce the related concepts of recursion.

One, the concept of recursion

  Generally speaking, the "maze" problem we usually encounter is solved by the idea of ​​recursion. Simply put: Recursion is a method that calls itself , passing in different variables each time it is called . Recursion helps programmers solve complex problems and also makes the code more concise. Generally, we use the recursive idea in the programming process , but the implementation of the code uses iteration . The most common one is that the three methods of deep traversal of binary trees (pre-order, middle-order, and post-order) all use recursive methods to achieve it. Next, we introduce the recursive calling mechanism:
  In order to let everyone better understand the recursive calling mechanism, we will use some small cases to help you understand the inner process of the call, the most commonly used recursive classic problems:

   Print problem
   factorial problem

  We first give the relevant code of the "printing problem", the specific code is as follows:

public static void main(String[] args){
    
    
	test(4);
}
public static void test(int n){
    
    
	if(n > 2){
    
    
		test(n - 1);
	}
	System.out.println("n = " + n);
}

  The recursive calling principle is as follows:

  1. When the program executes to a method, an independent space (stack) will be opened up.
  2. The data (local variables) of each space is independent

  The specific call diagram is shown in the following figure: The

  specific code implementation is as follows:

public class RecursionTest {
    
    
    public static void main(String[] args) {
    
    
        // TODO Auto-generated method stub
        //通过打印问题,回顾递归调用机制
        test(4);
        //int res = factorial(3);
        //System.out.println("res=" + res);
    }
    //打印问题.
    public static void test(int n) {
    
    
        if (n > 2) {
    
    
            test(n - 1);
        } //else {
    
    
        System.out.println("n=" + n);
        // }
    }
    //阶乘问题
    public static int factorial(int n) {
    
    
        if (n == 1) {
    
    
            return 1;
        } else {
    
    
            return factorial(n - 1) * n; // 1 * 2 * 3
        }
    }
}

  The effect of execution is as follows:

Two, recursive application

  After introducing the basic application of recursion, the following describes the specific problems that recursion can solve. details as follows:

  1. Various mathematical problems : such as the queen problem, the tower of Hanoi, the factorial problem, the maze problem, the ball and basket problem.
   2. Recursion is also used in various algorithms , such as fast sorting, merge sorting, binary search, divide and conquer Algorithm etc.
  3. Problems that will be solved with the stack ----->The recursive code is relatively concise

  The important principles that we should observe when using recursion are as follows:

  1. When a direction is executed, a new protected independent space (stack space) is created.
  2. The local variables of the method are independent and will not affect each other, such as n variables.
  3. If the method uses a reference type Variables (such as arrays) will share the data of the reference type.
  4. Recursion must be close to the condition of exiting recursion , otherwise it is infinite recursion. StackOverFlowError
  5. When a method is executed or encountered return, it will return. Follow who calls , The result will be returned to whom , and when the method is executed or returns, the method is also executed.

Three, recursion-maze problem

  This problem is relatively simple, so the code is directly given for everyone to learn:

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;
        }
        //设置挡板, 1 表示
        map[3][1] = 1;
        map[3][2] = 1;
//		map[1][2] = 1;
//		map[2][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();
        }

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

  The effect of the implementation is shown in the figure:

  Through the above realization, we continue to discuss the maze problem at a deeper level:

  1. The path obtained by the ball is related to the path finding strategy set by the programmer, namely: the up, down, left, and right order of finding the way is related.
  2. When finding the path of the path, you can use (bottom right, top left) first, and then change to (top right) Bottom left), see if the path has changed
  3. Test the backtracking phenomenon
  4. Finally, we should find the shortest path

Four, recursion-eight queens problem

  When we introduced the basic content of the data structure earlier, we briefly mentioned the eight queens problem . In fact, the problem of eight queens is that no two queens can be in the same row, column, or diagonal line. How many ways are there ?
Insert picture description here
  The analysis of the eight queens problem is as follows:

  1. The first queen is placed in the first row and the first column.
   2. The second queen is placed in the second row and the first column, and then it is judged whether it is OK [that is, it is a conflict]. If it is not OK, continue to be placed in the second column , The third column, put all the columns in order, find a suitable
  3, continue to the third queen, or the first column, the second column... Until the eighth queen can also be placed in a non-conflicting position, be regarded as 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, the first queen, put all the correct solutions in the first column, all the correct solutions are obtained.
  5. Then go back to the first queen to put the second column, and then continue to loop through the steps 1, 2, 3, and 4

  In theory, we should first create a two-dimensional array to represent the chessboard to realize the eight queens problem, but in fact, the problem can be solved by using a one-dimensional array through an algorithm. For example:, the arr[8] = {0, 4, 7, 5, 2, 6, 1, 3}corresponding subscript indicates the row, which is the queen arr[i] = val,, val indicates the i+1queen, put it in 第i+1行的第val+1列. Next, give the specific code of the eight queens problem:

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;
    static int judgeCount = 0;
    public static void main(String[] args) {
    
    
        //测试一把 , 8皇后是否正确
        Queue8 queue8 = new Queue8();
        queue8.check(0);
        System.out.printf("一共有%d解法", count);
        System.out.printf("一共判断冲突的次数%d次", judgeCount); // 1.5w

    }
    //编写一个方法,放置第n个皇后
    //特别注意: check 是 每一次递归时,进入到check中都有  for(int i = 0; i < max; i++),因此会有回溯
    private void check(int n) {
    
    
        if(n == max) {
    
      //n = 8 , 其实8个皇后就既然放好
            print();
            return;
        }
        //依次放入皇后,并判断是否冲突
        for(int i = 0; i < max; i++) {
    
    
            //先把当前这个皇后 n , 放到该行的第1列
            array[n] = i;
            //判断当放置第n个皇后到i列时,是否冲突
            if(judge(n)) {
    
     // 不冲突
                //接着放n+1个皇后,即开始递归
                check(n+1); //
            }
            //如果冲突,就继续执行 array[n] = i; 即将第n个皇后,放置在本行得 后移的一个位置
        }
    }
    //查看当我们放置第n个皇后, 就去检测该皇后是否和前面已经摆放的皇后冲突
    /**
     *
     * @param n 表示第n个皇后
     * @return
     */
    private boolean judge(int n) {
    
    
        judgeCount++;
        for(int i = 0; i < n; i++) {
    
    
            // 说明
            //1. array[i] == array[n]  表示判断 第n个皇后是否和前面的n-1个皇后在同一列
            //2. Math.abs(n-i) == Math.abs(array[n] - array[i]) 表示判断第n个皇后是否和第i皇后是否在同一斜线
            // n = 1  放置第 2列 1 n = 1 array[1] = 1
            // Math.abs(1-0) == 1  Math.abs(array[n] - array[i]) = Math.abs(1-0) = 1
            //3. 判断是否在同一行, 没有必要,n 每次都在递增
            if(array[i] == array[n] || Math.abs(n-i) == Math.abs(array[n] - array[i]) ) {
    
    
                return false;
            }
        }
        return true;
    }
    //写一个方法,可以将皇后摆放的位置输出
    private void print() {
    
    
        count++;
        for (int i = 0; i < array.length; i++) {
    
    
            System.out.print(array[i] + " ");
        }
        System.out.println();
    }
}

  The result of the execution is shown in the figure below:

to sum up

  The first two articles introduced the basic knowledge of the stack and related applications , mainly introduced the basic knowledge of the stack, the application scenarios of the stack, and the implementation of the stack. Finally, by comparing the difference between the stack and the queue, we can better let everyone Deeply understand the difference and connection between the two. Due to the wide range of applications of the stack, we have separately introduced a typical application of the stack through an article-the concrete realization of the conversion between inverse Polish expressions and infix expressions and postfix expressions, so as to let everyone understand and understand the stack. The application has a deeper understanding. This article introduces a more commonly used idea in data structure-recursion. It mainly introduces the related concepts of recursion and further illustrates the usage of recursion through some cases. In fact, data structures and algorithms are particularly important and play a vital role in programming. Therefore, we need special mastery. Life is endless and struggle is endless. We work hard every day, study hard, constantly improve our abilities, and believe that we will learn something. Come on! ! !

Guess you like

Origin blog.csdn.net/Oliverfly1/article/details/112691923