51.N 皇后
按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。
n 皇后问题 研究的是如何将 n
个皇后放置在 n×n
的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n
,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q'
和 '.'
分别代表了皇后和空位。
示例 1:
输入: n = 4
输出:[[“.Q…”,“…Q”,“Q…”,“…Q.”],[“…Q.”,“Q…”,“…Q”,“.Q…”]]
**解释:**如上图所示,4 皇后问题存在两个不同的解法。
示例 2:
输入: n = 1
输出:[[“Q”]]
提示:
- 1 ≤ n ≤ 9 1 \leq n \leq 9 1≤n≤9
解法一(回溯)
思路分析:
- 该题需要对放置皇后位置进行寻找,当找到的位置错误时,需要回退到确定错误位置前,即解决该题需要使用到回溯算法
- 即先确定回溯函数三要素:
- 确定函数的返回值和参数,因为该题需要记录所有可行的放置方案,所以不需要依靠返回值寻找最优,即返回值类型为
void
,同时对于参数,则需要count
记录当前放置到第几行,n
则记录最多放置多少行皇后 - 确定函数的结束条件,即当能够放完所有皇后
count == n
时,说明找到符合题意得放置方案,将其记录保存 - 确定函数得遍历过程,即从每一行得起始遍历到末尾,并判断当前位置是否能放置皇后,若不能放置则跳过,若能放置,则根据放置位置对周围位置做标记,并记录放置位置,然后继续寻找下一行皇后放置位置,寻找结束后,进行回溯,便于寻找第二可行方案。
- 确定函数的返回值和参数,因为该题需要记录所有可行的放置方案,所以不需要依靠返回值寻找最优,即返回值类型为
- 其中,标记某位置不能放置的数组的类型,一定要是数值型,因为可能会同时有两个及多个皇后标记某位置不能放置,若不是数值型,当回溯时,会因为一个皇后放置位置回溯,导致其余皇后标记的位置失效,从而得到不正确的方案
实现代码如下:
class Solution {
List<List<String>> ret = new LinkedList<>();
List<String> path = new LinkedList<>();
int[][] flag; // 用于标记几个皇后导致某位置不能放置 当0个皇后时 即flag[i][j]=0
boolean[][] ans; // 记录放置皇后的位置
public List<List<String>> solveNQueens(int n) {
flag = new int[n][n];
ans = new boolean[n][n];
backtracking(0, n);
return ret;
}
private void backtracking(int count, int n) {
if (count == n) {
// 找到符合题意的方案
path.clear(); // 清除之前保存的方案
for (int i = 0; i < n; ++i) {
StringBuilder str = new StringBuilder();
for (int j = 0; j < n; ++j) {
if (ans[i][j])
str.append('Q');
else str.append('.');
}
path.add(str.toString());
}
ret.add(new ArrayList<>(path));
return ;
}
for (int i = 0; i < n; ++ i) {
if (flag[count][i] != 0)
continue; // 跳过不能放置皇后的位置
doFlag(count, i, 1, n); // 根据当前位置 对不能放置皇后的位置作标记
ans[count][i] = true; // 记录放置皇后的位置
backtracking(count+1, n);
ans[count][i] = false; // 回溯
doFlag(count, i, -1, n);
}
}
// 根据皇后位置 做标记函数
private void doFlag(int i, int j, int value, int n) {
int k, l;
// 标记行
for (k = 0; k < n; ++ k)
flag[i][k] += value;
// 标记列
for (k = 0; k < n; ++ k) {
if (k == i) continue; // 避免重复标记
flag[k][j] += value;
}
// 当前位置到右下角
k = i; l = j;
while (++k < n && ++l < n)
flag[k][l] += value;
// 当前位置到右上角
k = i; l = j;
while (--k >= 0 && --l >= 0)
flag[k][l] += value;
// 当前位置到左下角
k = i; l = j;
while (++k < n && --l >= 0)
flag[k][l] += value;
// 当前位置到左上角
k = i; l = j;
while (--k >= 0 && ++l < n)
flag[k][l] += value;
}
}
提交结果如下:
解答成功:
执行耗时:2 ms,击败了89.69% 的Java用户
内存消耗:44.1 MB,击败了9.38% 的Java用户
复杂度分析:
- 时间复杂度:
O
(
n
!
)
O(n!)
O(n!),
n
表示皇后个数 - 空间复杂度: O ( n 2 ) O(n^2) O(n2)