题目:
n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
上图为 8 皇后问题的一种解法。(皇后攻击范围无限远,但只能攻击四个方向,水平、垂直
以及两条对角线方向)
给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。
每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。
示例:
输入: 4
输出: [
[".Q…", // 解法 1
“…Q”,
“Q…”,
“…Q.”],
["…Q.", // 解法 2
“Q…”,
“…Q”,
“.Q…”]
]
解释: 4 皇后问题存在两个不同的解法。
思路:
- 这是一个典型的用回溯解决的问题,也可以认为是深搜,个人理解深搜一般都伴随着回溯,可以认为每一行就是一层深度,最终目的是在每一行都放下一枚皇后,而他们不能互相攻击,简单的枚举所有摆放情况效率太低,通过分析已经摆放过的皇后,可以得出剩下的层中,哪些位置是不能摆放的,从而剪掉一些分支,提升效率;规则为:
- 每行放一个;
- 已经放了皇后的列,其他行在这一列不能再放皇后;
- 与已经放了皇后的左上到右下,右上到左下的斜边相交的其他行也不能放;
代码分析:
- 第一行随便放哪里都行,所以遍历每个位置,每次都进行深搜,搜到最后一层,如果满足要求就将结果存入结果集中;
- 每放一个皇后,我们都对棋盘上受到影响的位置进行标记,在下一曾摆放皇后的时候就避开这些位置,以提高效率,列的影响标记很好解决,n列只需要用n个长度的boolean数组即可标志哪些列是不能放的;
- 对角的标志则比较麻烦一些,如果把行看作y坐标,把列看作x坐标,则,左上到右下的格子的左边满足 x+y = c(常数),右上到左下 x-y = c(常数),那么我们只需要用x+y 或者 x - y来标记哪些斜线上已经放置了皇后,两个方向的斜线分别有2n-1条,我们使用2n长度的boolean数组:xySum及xyDif 即可记录;
- 注意回溯时恢复状态;
代码实现:
class Solution {
public List<List<String>> solveNQueens(int n) {
List<List<String>> result = new LinkedList<>();
List<String> list = new LinkedList<>();
DFS(result,list,0,new boolean[n],new boolean[2*n],new boolean[2*n],n);
return result;
}
/*
* cols 已经被占据的列,这些列不能再放置皇后
* xySum:已被占据的左上到右下的斜线
* xyDif:已被占据的右上到左下的斜线
*/
public void DFS(List<List<String>> result, List<String> list, int level,boolean[] cols,boolean[] xySum ,boolean[] xyDif,int n){
if(level >= n){
LinkedList<String> temp = new LinkedList<String>();
temp.addAll(list);
result.add(temp);
}
for(int i = 0;i < n;++i){
if(isValid(level,i,cols,xySum,xyDif,n)){
char[] buffer =new char[n];
Arrays.fill(buffer,'.');
buffer[i] = 'Q';
list.add(new String(buffer));
//标记已被占据的空间
setInvalid(level,i,cols,xySum,xyDif,n);
DFS(result,list,level+1,cols,xySum,xyDif,n);
//回溯,撤销影响
setValid(level,i,cols,xySum,xyDif,n);
list.remove(list.size()-1);
}
}
}
private void setInvalid(int level,int col,boolean[] cols,boolean[] xySum,boolean[] xyDif,int n){
cols[col] = true;
xySum[level+col] = true;
xyDif[level-col + n - 1] = true;
}
private void setValid(int level,int col,boolean[] cols,boolean[] xySum,boolean[] xyDif,int n){
cols[col] = false;
xySum[level+col] = false;
xyDif[level-col + n - 1] = false;
}
private boolean isValid(int level,int col, boolean[] cols,boolean[] xySum,boolean[] xyDif,int n){
if (cols[col]) return false;
if (xySum[level + col]) return false;
if (xyDif[level - col + n - 1]) return false;
return true;
}
}