算法-1-递归

一、递归

1、概念

某一个方法调用方法本身,就称之为递归(recursion

需要注意递归的次数,如果次数过多,可能会导致堆栈溢出( java.lang.StackOverflowError

使用递归,通过修改方法入参,不断尝试以达到预期结果,该过程被称之为回溯

这种方式优点是简单,缺点是可能回溯的执行次数过多

2、应用

【1】迷宫问题

在一个平面中(二维数组),四周都是墙体,不可以经过。有一个目标值,在某个位置。现需要从指定的位置,开始查找,问怎么查找可以到达目标位置

分析:

墙体不可以经过,已经经过的地方也不可以再走。要找到目标位置,在任意一点,都有4个方向可以走。所以可以使用回溯的思想,不断地重试 上、下、左、右 四个方向,直到到达目标位置

示例:

class Maze {
    
    
    private final int width;
    private final int length;
    private final int[][] map;

    /** 初始化坐标位置 */
    private final int INIT_X = 2;
    private final int INIT_Y = 2;

    /** 墙体 */
    private final int WALL = 1;

    /** 还未通过的地方 */
    private final int PATH = 0;

    /** 已经通过的地方 */
    private final int PASS = 2;

    /** 目标 */
    private final int TARGET = 9;

    /** 查找次数 */
    private int count = 0;

    public Maze(int width, int length) {
    
    
        if (width < 5 || length < 5) {
    
     // 宽高至少为5
            throw new IllegalArgumentException("非法宽高!");
        }

        this.width = width;
        this.length = length;
        this.map = new int[length][width];

        this.init();
    }

    private void init() {
    
    
        // 初始化墙体
        for (int i = 1, len = width - 1; i < len; i++) {
    
    
            map[0][i] = WALL;
            map[length - 1][i] = WALL;
        }
        for (int j = 0; j < length; j++) {
    
    
            map[j][0] = WALL;
            map[j][width - 1] = WALL;
        }

        for (int k = 1; k <= INIT_Y; k++) {
    
    
            map[INIT_X + 1][k] = WALL;
        }

        // 初始化目标
        map[length - 2][width - 2] = TARGET;
    }

    public void find() {
    
    
        find(INIT_X, INIT_Y);
    }

    private boolean find(int x, int y) {
    
    
        this.count++;

        if (TARGET == map[x][y]) {
    
    
            this.show();
            System.out.println("\n共查找了" + count + "次");
            return true;
        }

        if (PATH == map[x][y]) {
    
    
            map[x][y] = PASS;

            if (upFind(x, y)) {
    
    
                return true;
            } else if (rightFind(x, y)) {
    
    
                return true;
            } else if (downFind(x, y)) {
    
    
                return true;
            } else if (leftFind(x, y)) {
    
    
                return true;
            }
        }

        return false;
    }

    private boolean upFind(int x, int y) {
    
    
        return find(x - 1, y);
    }

    private boolean rightFind(int x, int y) {
    
    
        return find(x, y + 1);
    }

    private boolean downFind(int x, int y) {
    
    
        return find(x + 1, y);
    }

    private boolean leftFind(int x, int y) {
    
    
        return find(x, y - 1);
    }

    private void show() {
    
    
        for (int i = 0; i < length; i++) {
    
    
            for (int j = 0; j < width; j++) {
    
    
                System.out.print(map[i][j] + " ");
            }
            System.out.println();
        }
        System.out.println();
    }

}

【2】八皇后问题

在国际象棋棋盘上(8 * 8),摆放8个皇后,需要让每个皇后都互相不能攻击(水平、垂直 和 斜线方向不能同时存在2个皇后)。问一共有多少种摆法

分析:可以使用回溯的方法。用一个长度为8的数组表示8个皇后的水平(或垂直)位置,先通过循环数组的方式(下标即皇后摆放位置的 水平 或 垂直位置),得到当前皇后的可以摆放的位置,再通过不断地递归,得到下一个皇后的摆放位置,最终得到所有的摆法

示例:

class EightQueens {
    
    
    private static final int EIGHT = 8;
    private static int count = 0;
    private static int sum = 0;

    public static void pattern() {
    
    
        pattern(EIGHT);
    }

    public static void pattern(int length) {
    
    
        int[] array = new int[length];
        pattern(array, length, 0);

        System.out.println("一共有" + sum + "种摆法");
        System.out.println("一共校验了" + count + "次");
    }

    private static void pattern(int[] array, int length, int index) {
    
    
        if (length == index) {
    
    
            for (int i = 0; i < length; i++) {
    
    
                System.out.print(array[i] + " ");
            }
            System.out.println();
            sum++;
            return;
        }

        for (int i = 0; i < length; i++) {
    
    
            array[index] = i;
            if (check(array, index)) {
    
     // 循环判断当前索引位置是否可以摆放皇后
                pattern(array, length, index + 1); // 继续摆放下一个皇后
            }
        }
    }

    private static boolean check(int[] array, int index) {
    
    
        count++;
        for (int i = 0; i < index; i++) {
    
    
            // 校验水平或垂直位置是否有皇后存在,以及校验斜线方向是否存在皇后
            if (array[i] == array[index] || (index - i) == Math.abs(array[index] - array[i])) {
    
    
                return false;
            }
        }
        return true;
    }

}

猜你喜欢

转载自blog.csdn.net/adsl624153/article/details/103866013