1. 问题描述:
n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击
上图为 8 皇后问题的一种解法。
给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。
每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。
示例:
输入: 4
输出: [
[".Q..", // 解法 1
"...Q",
"Q...",
"..Q."],
["..Q.", // 解法 2
"Q...",
"...Q",
".Q.."]
]
解释: 4 皇后问题存在两个不同的解法。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/n-queens
2. 思路分析:
① 这是一道非常经典的使用dfs来解决的题目,因为dfs能够尝试所有的可能性,找到所有满足条件的答案并且一直到递归结束,这道题目的核心是检查当前即将要放置的皇后是否与之前放置的皇后存在冲突,需要检查主对角线(左上到右下角)、副对角线(右上到左下角)与当前放置的位置对应的列上是否存在皇后,不需要检查当前行是否存在皇后,因为当前行中只能够放置一个皇后,假如不存在冲突那么当前位置的皇后就可以放置在这个位置,一个比较经典的做法是使用一维数组来记录,一维数组的值表示的是当前的行对应的列,所以假如可以放置那么将这个位置附上列的值即可,之后我们在放置皇后的时候可以检查这个数组中放置的皇后即可
② 所以需要声明一个一维数组来记录,并且使用一维数组来记录的话也有一个好处,可以非常方便检查之前是否存在冲突,检查的图如下,对照下面的图我们就可以轻松写出判断皇后冲突的代码,因为可以从他们的位置得到对应的几何关系:
依次检查主对角线、副对角线、列是否存在冲突:
③ 解决了上面的问题之后那么剩下来的就比较简单了,我们需要再方法中传入当前的行,在for循环中表示的是当前行对应的列,当放置好当前的皇后之后,递归下一行,尝试下一行皇后的放置
④ 由于返回值为List<List<String>>类型所以我们可以借助之前的思路,可以使用stack来记录中间结果最后加入到结果集中即可,不得不说这个方法真的很方便,可以将栈的元素直接丢给List
3. 代码如下:
class Solution {
int rec[];
public List<List<String>> solveNQueens(int n) {
rec = new int[n];
recursion(0, new Stack<String>());
return res;
}
List<List<String>> res = new ArrayList<>();
private void recursion(int pos, Stack<String> stack) {
if (pos == rec.length){
res.add(new ArrayList<>(stack));
return;
}
/*for循环中的变量表示的列*/
for (int i = 0; i < rec.length; ++i){
if (check(pos, i)){
/*符合要求放置皇后*/
rec[pos] = i;
String t = "";
for (int j = 0; j < rec.length; ++j){
if (j != i){
t += ".";
}else {
t += "Q";
}
}
stack.add(t);
recursion(pos + 1, stack);
stack.pop();
}
}
}
/*检查主对角线/副对角线/列是否存在皇后*/
private boolean check(int r, int c) {
for (int i = 0; i < r; ++i){
if (i - rec[i] == r - c || i + rec[i] == r + c || rec[i] == c){
return false;
}
}
return true;
}
}