题目描述
n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。

题解
回溯算法(深度优先遍历)
递归的深度就是棋盘的高度(行数),棋盘的宽度就是for循环的长度
递归终止条件:当递归到棋盘最底层(也就是叶子节点)的时候,就可以收集结果并返回了
按照如下标准去重:不能同行,不能同列,不能同斜线 (45度和135度角)
先尝试画出递归树,以 4 皇后问题为例,画出的递归树如下:

搜索的过程蕴含了 剪枝 的思想。「剪枝」的依据是:题目中给出的 「N 皇后」 的摆放规则:1、不在同一行;2、不在同一列;3、不在同一主对角线方向上;4、不在同一副对角线方向上。
小技巧:记住已经摆放的皇后的位置
这里记住已经摆放的位置不能像 Flood Fill 一样,简单地使用 visited 布尔数组。放置的规则是:一行一行考虑皇后可以放置在哪一个位置上,某一行在考虑某一列是否可以放置皇后的时候,需要根据前面已经放置的皇后的位置。
由于是一行一行考虑放置皇后,摆放的这些皇后肯定不在同一行,为了避免它们在同一列,需要一个长度为 N 的布尔数组 cols,已经放置的皇后占据的列,就需要在对应的列的位置标注为 True。
考虑对角线(找规律)
下面我们研究一下主对角线或者副对角线上的元素有什么特性。在每一个单元格里写下行和列的 下标。

为了保证至少两个皇后不同时出现在 同一主对角线方向 或者 同一副对角线方向。检查策略是,只要「检测」到新摆放的「皇后」与已经摆放好的「皇后」冲突,就尝试摆放同一行的下一个位置,到行尾还不能放置皇后,就退回到上一行。
可以像全排列 used 数组那样,再为 「主对角线(Main diagonal)」 和 「副对角线(Sub diagonal)」 设置相应的 布尔数组变量,只要排定一个 「皇后」 的位置,就需要占住对应的位置。
编码
我们使用一个 1 到 4 的排列表示一个 4×4 的棋盘,例如:

得到一个符合要求的全排列以后,生成棋盘的代码就很简单了。
dfs:
1.参数:row,path
row代表遍历到了第几行,path代表当前的摆放序列,path中是每一行的皇后的纵坐标j
2.递归终止条件:row==n
3.如何递归到下一步:dfs(row+1,path)
4.如何回溯:
sub[row+j]=false;
main[row-j+n-1]=false;
col[j]=false;
path.removeLast();
class Solution {
private int n;
//记录某一列是否放置了皇后
private boolean[] col;
//记录主对角线上的单元格是否放置了皇后
private boolean[] main;
//记录副对角线上的单元格是否放置了皇后
private boolean[] sub;
private List<List<String>> res;
public List<List<String>> solveNQueens(int n) {
res = new ArrayList<>();
if(n==0) return res;
this.n = n;
this.col = new boolean[n];
//同一个方向上有2*n-1条对角线
this.main = new boolean[2 * n - 1];
this.sub = new boolean[2 * n - 1];
LinkedList<Integer> path = new LinkedList<>();
dfs(0, path);
return res;
}
//row: path
private void dfs(int row,LinkedList<Integer> path){
//深度优先遍历到下标为n,表示[0...n-1]已经填完,得到了一个结果
if(row==n){
List<String> board = convert2board(path);
res.add(board);
return;
}
//针对下标为row的每一列,尝试是否可以放置
for(int j=0;j<n;j++){
if(!col[j]&&!main[row-j+n-1]&&!sub[row+j]){
path.addLast(j);
col[j]=true;
main[row-j+n-1]=true;
sub[row+j]=true;
dfs(row+1,path);
sub[row+j]=false;
main[row-j+n-1]=false;
col[j]=false;
path.removeLast();
}
}
}
private List<String> convert2board(LinkedList<Integer> path){
List<String> board = new ArrayList<>();
for(Integer num:path){
StringBuilder row = new StringBuilder();
row.append(".".repeat(Math.max(0,n)));
row.replace(num,num+1,"Q");
board.add(row.toString());
}
return board;
}
}
本文解析了n皇后问题的回溯算法解法,利用深度优先搜索策略,通过行、列和对角线的约束进行剪枝,详细介绍了数据结构的使用以及如何确保皇后间互不攻击。适合理解递归和解决经典计算机科学问题。

607

被折叠的 条评论
为什么被折叠?



