N皇后-leetcode

题目描述

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

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

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

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

示例 1:

img

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

示例 2:

输入:n = 1
输出:[["Q"]]

提示:

  • 1 <= n <= 9

解法一

思路:

回溯方法,每行遍历放置皇后的位置,然后判断他是否可以放置皇后,可以放置就添加上皇后,那么就遍历下一行的位置,回溯的时候需要恢复之前的状态。用一个jList列表记录所有的列坐标,对于新的位置,首先排除列重复,之后对左斜向和右斜向进行判断。

代码:

class Solution {
    private List<List<String>> res = new ArrayList<>();
    public List<List<String>> solveNQueens(int n) {
        char[][] board = new char[n][n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                board[i][j] = '.';
            }
        }

        backtrack(board,new ArrayList<>(),0,new ArrayList<>());
        return res;


    }

    public void backtrack(char[][] board, List<String> cur,int i,List<Integer> jList) {
        //终止条件
        if(i==board.length){
            res.add(new ArrayList<>(cur));
            return;
        }
        for(int j=0;j<board.length;j++){
            if(board[i][j]=='.'&&check(board,jList,i,j)){
                board[i][j]='Q';
                jList.add(j);
                cur.add(new String(board[i]));
                backtrack(board,cur,i+1,jList);
                //回溯
                board[i][j]='.';
                jList.remove(jList.size()-1);
                cur.remove(cur.size()-1);
            }
        }
    }

    public boolean check(char[][] board, List<Integer> jList, int i,int j){
        if(jList.contains(j)) return false;
        //斜向左方向
        for(int k1=i,k2=j;k1>=0&&k2>=0;k1--,k2--){
            if(board[k1][k2]=='Q')return false;
        }

        //斜向右方向
        for(int k1=i,k2=j;k1>=0&&k2<board.length;k1--,k2++){
            if(board[k1][k2]=='Q')return false;
        }
        return true;
    }
}

解法二

思路:

为了判断一个位置所在的列和两条斜线上是否已经有皇后,使用三个集合 columns、diagonals1 和 diagonals2 分别记录每一列以及两个方向的每条斜线上是否有皇后。

列的表示法很直观,一共有 N 列,每一列的下标范围从 0 到 N−1,使用列的下标即可明确表示每一列。

如何表示两个方向的斜线呢?对于每个方向的斜线,需要找到斜线上的每个位置的行下标与列下标之间的关系。

方向一的斜线为从左上到右下方向,同一条斜线上的每个位置满足行下标与列下标之差相等,例如 (0,0) 和 (3,3) 在同一条方向一的斜线上。因此使用行下标与列下标之差即可明确表示每一条方向一的斜线。

fig1

方向二的斜线为从右上到左下方向,同一条斜线上的每个位置满足行下标与列下标之和相等,例如 (3,0) 和 (1,2) 在同一条方向二的斜线上。因此使用行下标与列下标之和即可明确表示每一条方向二的斜线。

fig2

每次放置皇后时,对于每个位置判断其是否在三个集合中,如果三个集合都不包含当前位置,则当前位置是可以放置皇后的位置。

对角线方向 \ (diagonals1)

规律:在这条斜线上的所有点,它们的 横坐标(row) 减去 纵坐标(col/i) 的差是一个固定值。

举个例子: 假设棋盘大小是 4x4,我们看一条经过 (0,0), (1,1), (2,2), (3,3) 的斜线 \

  • 0 - 0 = 0
  • 1 - 1 = 0
  • 2 - 2 = 0
  • 3 - 3 = 0 差值都是 0

再看一条经过 (0,1), (1,2), (2,3) 的斜线 \

  • 0 - 1 = -1
  • 1 - 2 = -1
  • 2 - 3 = -1 差值都是 -1

代码逻辑: 当代码执行到 int diagonal1 = row - i; 时,计算的就是当前格子所在的 \ 方向斜线的“专属编号”(差值)。 如果 diagonals1.contains(diagonal1) 返回 true,说明之前已经有一个皇后放在了这条差值相同的斜线上,因此当前位置不能放皇后。

代码:

class Solution {
    public List<List<String>> solveNQueens(int n) {
        List<List<String>> solutions = new ArrayList<List<String>>();
        int[] queens = new int[n];
        Arrays.fill(queens, -1);
        Set<Integer> columns = new HashSet<Integer>();
        Set<Integer> diagonals1 = new HashSet<Integer>();
        Set<Integer> diagonals2 = new HashSet<Integer>();
        backtrack(solutions, queens, n, 0, columns, diagonals1, diagonals2);
        return solutions;
    }

    public void backtrack(List<List<String>> solutions, int[] queens, int n, int row, Set<Integer> columns, Set<Integer> diagonals1, Set<Integer> diagonals2) {
        if (row == n) {
            List<String> board = generateBoard(queens, n);
            solutions.add(board);
        } else {
            for (int i = 0; i < n; i++) {
                if (columns.contains(i)) {
                    continue;
                }
                int diagonal1 = row - i;
                if (diagonals1.contains(diagonal1)) {
                    continue;
                }
                int diagonal2 = row + i;
                if (diagonals2.contains(diagonal2)) {
                    continue;
                }
                queens[row] = i;
                columns.add(i);
                diagonals1.add(diagonal1);
                diagonals2.add(diagonal2);
                backtrack(solutions, queens, n, row + 1, columns, diagonals1, diagonals2);
                queens[row] = -1;
                columns.remove(i);
                diagonals1.remove(diagonal1);
                diagonals2.remove(diagonal2);
            }
        }
    }

    public List<String> generateBoard(int[] queens, int n) {
        List<String> board = new ArrayList<String>();
        for (int i = 0; i < n; i++) {
            char[] row = new char[n];
            Arrays.fill(row, '.');
            row[queens[i]] = 'Q';
            board.add(new String(row));
        }
        return board;
    }
}
posted @ 2026-03-30 17:54  狐狸胡兔  阅读(2)  评论(0)    收藏  举报