【92.N皇后】

176 阅读2分钟

题目

按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 **n 皇后问题 的解决方案。

每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。

示例 1:

输入: n = 4
输出: [[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
解释: 如上图所示,4 皇后问题存在两个不同的解法。

题解

方式一:暴力

step1:遍历棋盘第一行,确定第一个皇后
step2:按规则更新棋盘,将可攻击的位置禁用
step3:直到皇后数量达到n

方式二:回溯

也可以利用下标计算判断是否能选择当前列

List<List<String>> result = new ArrayList<>();
Set<Integer> cols = new HashSet<>(); // 哪一列有皇后
Set<Integer> set1 = new HashSet<>(); // 向下斜线有皇后  row - col = 固定值
Set<Integer> set2 = new HashSet<>(); // 向上斜线有皇后  row + col = i

public List<List<String>> solveNQueens(int n) {
    int[] queens = new int[n]; // 每一行 皇后的下标
    Arrays.fill(queens, -1);
    backtrack(queens, n, 0);
    return result;
}

public void backtrack(int[] queens, int n, int row) {
    if (row == n) {
        result.add(getBoard(queens, n));
        return;
    } 
    // 遍历row行所有列,选择不被攻击的列放皇后
    for (int i = 0; i < n; i++) {
        if (cols.contains(i)) {
            continue;
        }
        int x = row - i;
        if (set1.contains(x)) {
            continue;
        }
        int y = row + i;
        if (set2.contains(y)) {
            continue;
        }
        // 三条线都没有皇后,当前列可以选择
        queens[row] = i;
        cols.add(i); // row 行 i 列 已经存在皇后
        set1.add(x); // 行 - 列 差值为x的斜线上已经存在皇后
        set2.add(y); // 行 + 列 和为y的斜线上已经存在皇后
        backtrack(queens, n, row + 1); // 进入下一行
        queens[row] = -1;
        cols.remove(i);
        set1.remove(x);
        set2.remove(y);
    }
}

public List<String> getBoard(int[] queens, int n) {
    List<String> board = new ArrayList<>();
    for (int i = 0; i < n; i++) {
        char[] row = new char[n];
        Arrays.fill(row, '.');
        row[queens[i]] = 'Q'; // 第i行皇后的下标 = queens[i]
        board.add(new String(row));
    }
    return board;
}

总结

算法:回溯