アルゴリズム-1-再帰

1つ、再帰

1.コンセプト

特定のメソッドはメソッド自体を呼び出します。これは再帰(recursionと呼ばれます。

再帰の数に注意する必要があります。回数が多すぎると、スタックオーバーフローが発生する可能性があります(java.lang.StackOverflowError

再帰を使用し、メソッドを変更してパラメーターを入力し、目的の結果を達成しようとし続けます。このプロセスはバックトラッキングと呼ばれます。

この方法の利点は単純ですが、欠点は、バックトラッキングの実行が多すぎる可能性があることです。

2.アプリケーション

[1]迷路の問題

平面(2次元配列)では、周囲に壁があり、通り抜けることができません。特定の位置に目標値があります。次に、指定した場所から検索を開始し、ターゲットの場所を見つける方法を尋ねる必要があります

分析:

壁を通り抜けることはできず、すでに通り過ぎた場所に行くこともできません。目的の場所を見つけるには、任意の時点で進むべき4つの方向があります。したがって、バックトラック思考を使用して上、下、左、右、目標位置に到達するまで常に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