n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回 n 皇后问题 不同的解决方案的数量。
示例 1:
输入:n = 4
输出:2
解释:如上图所示,4 皇后问题存在两个不同的解法。
示例 2:
输入:n = 1
输出:1
提示:
1 <= n <= 9
皇后彼此不能相互攻击,也就是说:任何两个皇后都不能处于同一条横行、纵行或斜线上。
方法1
一行一行的放,一列一列的试探
利用三个集合保存列和两条对角线的元素存放情况
时间复杂度:O(N!),其中 N 是皇后数量。
空间复杂度:O(N),其中 N 是皇后数量。空间复杂度主要取决于递归调用层数以及三个集合,递归调用层数不会超过 N,每个集合的元素个数都不会超过 N。
class Solution {
public int totalNQueens(int n) {
if(n <= 0){
return 0;
}
Set<Integer> columns = new HashSet<>(n);
Set<Integer> diagonals1 = new HashSet<>(n);
Set<Integer> diagonals2 = new HashSet<>(n);
//从第0行开始向下试探
return dfs(0, n, columns, diagonals1, diagonals2);
}
//递归回溯
public int dfs(int row, int n, Set<Integer> columns, Set<Integer> diagonals1, Set<Integer> diagonals2){
if(row == n){
//到底了,说明这种方案可行
return 1;
}
else{
int count = 0;
//一列一列的试探
for(int col = 0; col < n; ++col){
if(columns.contains(col)){
continue;
}
//左上右下对角线的row-col为常数
int diagonal1 = row - col;
if(diagonals1.contains(diagonal1)){
continue;
}
//右上左下对角线的row+col为常数
int diagonal2 = row + col;
if(diagonals2.contains(diagonal2)){
continue;
}
//当前列可以放置皇后
columns.add(col);
diagonals1.add(diagonal1);
diagonals2.add(diagonal2);
//递归计算所有可行解的数量
count += dfs(row + 1, n, columns, diagonals1, diagonals2);
//回溯
columns.remove(col);
diagonals1.remove(diagonal1);
diagonals2.remove(diagonal2);
}
return count;
}
}
}
方法2
位运算牛逼!
需要注意的地方在于:皇后所在的整个一条对角线都不能放其他皇后了,所以对角线里❌和皇后所在位置都为1,然后一起向右或者向左移动一格来计算下一行不能放置的位置;其他内容看题解和注释
时间复杂度:O(N!),其中 N是皇后数量。
空间复杂度:O(N),其中 N 是皇后数量。由于使用位运算表示,因此存储皇后信息的空间复杂度是 O(1),空间复杂度主要取决于递归调用层数,递归调用层数不会超过 N。
class Solution {
public int totalNQueens(int n) {
if(n <= 0){
return 0;
}
//从第0行开始向下试探,初始时没有放皇后,三个整数都是0;注意java int类型占32位
return dfs(0, n, 0, 0, 0);
}
//递归回溯
public int dfs(int row, int n, int columns, int diagonals1, int diagonals2){
if(row == n){
//到底了,说明这种方案可行
return 1;
}
else{
int count = 0;
//计算可用的位置;2^n-1可以得到n个1,计算只用到n位,其他位置0
int positions = ((1 << n) - 1) & (~(columns | diagonals1 | diagonals2));
while(positions != 0){
//只留下最后一个1:
int position = positions & (-positions);
//更新三个整数并递归统计解的个数
count += dfs(row + 1, n, columns|position, (diagonals1|position)>>1, (diagonals2|position)<<1);
//将最后一个1变成0:
positions &= (positions - 1);
//不用回溯,因为下一行的三个整数是传入的计算后的值,但是这三个整数本身没改变,还能继续用
}
return count;
}
}
}