設計一種算法,打印 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;
}
}