思路:
要求简化为:任何两个皇后都不能在同一行、同一列以及同一条斜线上(包括正对角线和反对角线)。
从行row开始递增,设置三个集合记录列位置,正对角线位置,反对角线位置。
方向一的斜线(正对角线)为从左上到右下方向,同一条斜线上的每个位置满足行下标与列下标之差相等,例如 (0,0) 和 (3,3) 在同一条方向一的斜线上。因此使用行下标与列下标之差即可明确表示每一条方向一的斜线。
方向二的斜线(反对角线)为从右上到左下方向,同一条斜线上的每个位置满足行下标与列下标之和相等,例如 (3,0) 和 (1,2) 在同一条方向二的斜线上。因此使用行下标与列下标之和即可明确表示每一条方向二的斜线。
每次放置皇后时,对于每个位置判断其是否在三个集合中,如果三个集合都不包含当前位置,则当前位置是可以放置皇后的位置。
代码:
leetcode链接面试题 08.12. 八皇后 - 力扣(LeetCode)
//N皇后问题求解
public class Solution {
//判断一个位置所在的列和两条斜线上是否已经有皇后,使用三个集合
// columns、diagonals1 和 diagonals2
//分别记录每一列以及两个方向的每条斜线上是否有皇后。
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,columns,diagonals1,diagonals2,n,0);
return solutions;
}
public void backtrack(List<List<String>> solutions, int[] queens,
Set<Integer> columns,Set<Integer> diagonals1,
Set<Integer> diagonals2,
int n ,int row){
if(row == n){
solutions.add(generateBoard(queens,n));
}else{
for(int i = 0 ; i < n ; i ++){
//三个集合有值-代表该位置不可用
if(columns.contains(i)){
continue;
}
int diagonal1 = row - i;
int diagonal2 = row + i;
if(diagonals1.contains(diagonal1)){
continue;
}
if(diagonals2.contains(diagonal2)){
continue;
}
//标记该位置可以使用,更新集合
queens[row]= i;
columns.add(i);
diagonals1.add(diagonal1);
diagonals2.add(diagonal2);
//下一步回溯
backtrack(solutions,queens,columns,diagonals1,diagonals2,n,row+1);
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;
}
}