回溯算法 - n 皇后问题

(1)问题描述

  在 n × n 格的棋盘上放置彼此不受攻击的 n 个皇后。按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。n 后问题等价于在 n × n 的棋盘上放置 n 个皇后,任何 2 个皇后不放在同一行或同一列或同一斜线上。

  

(2)算法描述

  a. 将第一个皇后放置在第一行的第一个空格里;

  b. 对于第二行,从第一个空格开始寻找不与第一行的皇后冲突的空格。找到的第一个不冲突的空格是第2个;

  c. 对于第三行,这时已经找不到与之前放置的两个皇后不冲突的空格了。把当前行恢复初始状态,返回到上一行;

  d. 在当前行皇后所占的空格之后寻找一个不与之前皇后冲突的位置。有两种情况,如果找到了则把当前行的皇后移动到该位置,然后处理下一行。如果直到最后当前行的最后一个空格也没有找合适的位置,则把当前行恢复初始状态,继续回溯到上一行;

  e. 把最后一个皇后成功安置在最后一行,代表找到了一种可行解。返回步骤 d ;

  f. 当需要回溯到第 0 行的时候代表已经找遍了所有可能的可行解。

(3)算法代码

public class NQueen {

    /**
     * 皇后数量
     */
    private static Integer num;

    /**
     * 可行解的总数
     */
    private static Integer sum = 0;

    /**
     * n 皇后当前解
     */
    private static Integer[] answer;

    /**
     * 初始化数据
     */
    private static void initData() {
        Scanner input = new Scanner(System.in);
        System.out.println("请输入 n 皇后数量:");
        num = input.nextInt();

        answer = new Integer[num];
    }

    /**
     * 判断当前皇后存放是否可行
     */
    private static Boolean bound(int t) {
        for (int i = 0; i < t; i++) {   // 判断第 t 个皇后和前面已经存放好的第 (0 ~ num -1) 个皇后之间是否存在同列同斜率
            /**
             * 1)Math.abs(t - i) 表示两点的纵坐标;
             * 2)Math.abs(answer[t] - answer[i]) 表示两点的横坐标;
             * 3)answer[t] == answer[i] 表示两个皇后是否同列;
             */
            if ((Math.abs(t - i) == Math.abs(answer[t] - answer[i])) || (answer[t] == answer[i])) {
                return false;
            }
        }
        return true;
    }

    /**
     * 回溯求解 n 皇后问题
     */
    private static void backtrack(int t) {
        if (t == num) {                                 // 第 n 个皇后已经填完毕,满足条件
            // 输出当前可行解
            Stream.of(answer).forEach(element -> System.out.print(element + " "));
            System.out.println();
            sum++;
            return;
        }
        for (int j = 0; j < num; j++) {     // 将第 t 个皇后依次放入 (0 ~ num - 1) 个位置进行判定
            answer[t] = j;                  // 将第 t 个皇后放入 j 位置
            if (bound(t)) {                 // 判断将第 t 个皇后放入 j 位置,是否符合条件
                backtrack(t + 1);
            }
        }
    }

    public static void main(String[] args) {
        // 初始化数据
        initData();

        // 回溯求解 n 皇后问题
        backtrack(0);
        
        System.out.println("可行性解总数: sum = " + sum);
    }

}
n皇后算法核心代码

(4)输入输出

请输入 n 皇后数量:
5
0 2 4 1 3 
0 3 1 4 2 
1 3 0 2 4 
1 4 2 0 3 
2 0 3 1 4 
2 4 1 3 0 
3 0 2 4 1 
3 1 4 2 0 
4 1 3 0 2 
4 2 0 3 1 
可行性解总数: sum = 10
View Code

(5)总结

  n 皇后问题同样提现了回溯算法的核心思想,依次深度搜索,回溯到上一层;但是不与 子集树、排序树相同,有一定的区别,每一个皇后寻找位置都是从头依次找合适的位置,直到行尾才结束,然后回溯到上一层;时间复杂度为:O(nn);

  同样希望大家能动手实践一下,画一画走一下代码流程,加深回溯算法的思想。

posted @ 2020-02-13 15:08  菜鸟的奋斗之路  阅读(1076)  评论(0编辑  收藏  举报