N皇后-leetcode
题目描述
按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。
n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。
示例 1:

输入: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) 在同一条方向一的斜线上。因此使用行下标与列下标之差即可明确表示每一条方向一的斜线。

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

每次放置皇后时,对于每个位置判断其是否在三个集合中,如果三个集合都不包含当前位置,则当前位置是可以放置皇后的位置。
对角线方向 \ (diagonals1)
规律:在这条斜线上的所有点,它们的 横坐标(row) 减去 纵坐标(col/i) 的差是一个固定值。
举个例子: 假设棋盘大小是 4x4,我们看一条经过 (0,0), (1,1), (2,2), (3,3) 的斜线 \:
0 - 0 = 01 - 1 = 02 - 2 = 03 - 3 = 0差值都是0。
再看一条经过 (0,1), (1,2), (2,3) 的斜线 \:
0 - 1 = -11 - 2 = -12 - 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;
}
}

浙公网安备 33010602011771号