设计一种算法,打印 N
皇后在 N × N
棋盘上的各种摆法,其中每个皇后都不同行、不同列,也不在对角线上。这里的“对角线”指的是所有的对角线,不只是平分整个棋盘的那两条对角线。
注意:本题相对原题做了扩展
示例:
输入:4
输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
解释: 4 皇后问题存在如下两个不同的解法。
[
[".Q..", // 解法 1
"...Q",
"Q...",
"..Q."],
["..Q.", // 解法 2
"Q...",
"...Q",
".Q.."]
]
思路:
- 对于这种全部排列的问题,显然是回溯法。
- 回溯法就是一棵决策树。
- 通过循环中的条件判断进行剪枝。
- 八皇后每一行、每一列、每一条对角线都只能有一个Q(皇后)。
- 本题扩展到了N皇后。
伪代码如下:
行 = 0;
从第一列开始:
如果这个点不合法:跳过。
否则:
本点放个皇后;
回溯(下一行);
撤销选择,删掉本点的皇后。
boolean 是否合法:
判断此列是否有皇后;
判断左上对角线是否有皇后;
判断右上对角线是否有皇后;
返回判断结果。
代码:
class Solution {
public List<List<String>> solveNQueens(int n) {
List<List<String>> result = new ArrayList<>();
//定义棋盘
char[][] qipan = new char[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
qipan[i][j] = '.';
}
}
//从第0行开始回溯
back_track(result,qipan,0,n);
return result;
}
public void back_track(List<List<String>> result,char[][] qipan, int hang, int n){
//如果到了最后一行,就结束本轮。
if(hang == n){
List<String> tmp = new ArrayList<>();
for (int i1 = 0; i1 < n; i1++) {
StringBuilder s = new StringBuilder();
for (int i = 0; i < n; i++) {
s.append(qipan[i1][i]);
}
tmp.add(s.toString());
}
result.add(tmp);
return;
}
//遍历选择
for(int lie = 0; lie<n;lie++){
//判断本位置是否合法,剪枝
if(!isValid(qipan,hang,lie,n)) continue;
//做选择
qipan[hang][lie] = 'Q';
//递归给下一行赋值
back_track(result,qipan,hang+1,n);
//撤销选择
qipan[hang][lie] = '.';
}
}
public boolean isValid(char[][] qipan, int hang, int lie, int n){
//当前列不能有Q
// for (int i = 0; i < n; i++) {
// //判断第hang行的n列
// if(qipan[hang][i] == 'Q') return false;
// }
for (int i = 0; i < n; i++) {
//判断第lie行的n行
if (qipan[i][lie] == 'Q') return false;
}
//左上角不能有Q
//l是左侧位置,u是上方。
int l = lie - 1;
int u = hang - 1;
while(l>=0 && u>=0){
if(qipan[u][l] == 'Q') return false;
l--;
u--;
}
//右上角不能有Q
//l是左侧位置,u是上方。
int r = lie + 1;
u = hang - 1;
while(r<n && u>=0){
if(qipan[u][r] == 'Q') return false;
r++;
u--;
}
return true;
}
}