八皇后问题的递归解法(最易理解的版本)


八皇后问题是一个古来而著名的问题,该问题是19世纪著名的数学家高斯同学提出来的。在8*8的国际象棋上摆放八个皇后,使其不能互相的攻击,也就是说,任意的两个皇后不能放在同一行或则是同一个列或者是同一个对角线上,问有多少个摆放的方法


本算法的思路是按行来规定皇后位置,第一行放置一个皇后,第二行放置一个皇后, 第N行也放置一个皇后… 这样, 可以保证每行都有一个皇后,那么各行的皇后应该放置在那一列呢, 算法通过循环来完成,在循环的过程中, 一旦找到一个合适的列,则该行的皇后位置确定,则继续进行下一行的皇后的位置的确定。由于每一行确定皇后位置的方式相似,所以可以使用递归法。一旦最后 一行的皇后位置确定,则可以得到一组解。找到一组解之后, 之前确定皇后应该放置在哪一列的循环其实才进行了一轮循环的, 算法通过该循环遍历所有的列,以此确定每一行所有可能的列的位置。在从一轮循环进入下一轮循环之前,算法需要清除在上一轮被标记为不可放置皇后的标记,也就是回溯。因为进入下一轮循环之后,同一行的皇后的列的位置会发生了变化,之前被标记为不可放置皇后的列和正反对角线位置都已经失效。

 
  

public class EightQueenSolver {


    private int QUEEN_COUNT = 0; // 皇后的默认数量

    private int[][] Queencount;// 分配8X8的数组,充当棋盘,存放皇后

    private int resultCount = 0;// 记录皇后的放置方法的总数

    private int[] Queenplace;// 对于索引n, Queenplace[n]表示第n行的皇后放置位置是第Queenplace[n]列

    public EightQueenSolver(int n) {
        this.QUEEN_COUNT = n;
        this.resultCount = 0;
        this.Queencount = new int[QUEEN_COUNT][QUEEN_COUNT];
        Queenplace = new int[QUEEN_COUNT];
    }

    public void putQueen() {
        putQueen(0);
    }

    private void putQueen(int row0) {
        int row = row0;// 行标
        for (int column = 0; column < QUEEN_COUNT; column++)// column表示列标,该层循环的作用是用于询问第row行,第column列是否可以放置皇后
        {
            if (Queencount[row][column] == 0)// 如果第row行、第column列可以放皇后
            {
                for (int nextRow = row + 1; nextRow < QUEEN_COUNT; nextRow++)// 该层for循环的作用是使其斜下方和正下方不为0
                {
                    //将不同行的同一列(正下方)标记为非零,表示不能再在该列放置皇后了
                    Queencount[nextRow][column]++;

                    //通过该层循环将第row行、第column列的正对角线上的位置标记为非零,表示不能在这些位置放置皇后
                    if (column - nextRow + row >= 0) {
                        Queencount[nextRow][column - nextRow + row]++;
                    }

                    //通过该层循环将第row行、第column列的反对角线上的位置标记为非零,表示不能在这些位置放置皇后
                    if (column + nextRow - row < QUEEN_COUNT) {
                        Queencount[nextRow][column + nextRow - row]++;
                    }
                }
                // 记录下第row行第column列放置了皇后
                Queenplace[row] = column;

                // 如果各行都放置了皇后,也就是说如果皇后已放满,打印出皇后布局
                if (row == QUEEN_COUNT - 1)
                {
                    printQueen(++resultCount);
                } else // 否则递归继续排列下一行皇后
                {
                    putQueen(row + 1);
                }
                for (int rows = row + 1; rows < QUEEN_COUNT; rows++)// 回溯,使得在第row行的皇后不放在第column列,那放置在那一列? 
                                                                    // 答案是通过该算法的最外层循环,利用最外层for循环将皇后放在这一行的其他列
                {
                    //既然第row行、第column列不放置皇后了,则需要恢复正下方的不可用标记,将不同行的同一列的非零标记还原,也即恢复该位置的正下面的标记
                    Queencount[rows][column]--;

                    //还原第row行、第column列的正对角线上的位置标记
                    if (column - rows + row >= 0) {
                        Queencount[rows][column - rows + row]--;
                    }
                    //还原第row行、第column列的反对角线上的位置标记
                    if (column + rows - row < QUEEN_COUNT) {
                        Queencount[rows][column + rows - row]--;
                    }
                }
            }
        }
        if (row == 0) {
            System.out.println(QUEEN_COUNT + "皇后问题共有" + resultCount + "个解.");
        }
    }

    private void printQueen(int size)// 打印皇后布局
    {
        System.out.println(QUEEN_COUNT + "皇后的第" + size + "个解是:");
        System.out.println();
        for (int row = 0; row < QUEEN_COUNT; row++) {
            for (int column = 0; column < QUEEN_COUNT; column++) {
                System.out.print(Queenplace[row] == column ? " * " : " - ");
            }
            System.out.println();
        }
        System.out.println();
    }

    public static void main(String[] args) {
        EightQueenSolver eq = new EightQueenSolver(8);
        eq.putQueen();
    }
}

你可能感兴趣的:(算法)