52.N 皇后 II
标签: backtracking
难度: Hard
通过率: 75.5%
原题链接: https://leetcode.com/problems/n-queens-ii/description/
题目描述
n 皇后问题是指将 n 个皇后放置在 n x n 的棋盘上,使得任何两个皇后都不能相互攻击的问题。给定整数 n,返回 n 皇后问题不同的解决方案的数量。
解题思路
N 皇后问题是经典的回溯问题。在 n x n 的棋盘上放置 n 个皇后,并确保任何两个皇后都不在同一行、同一列和同一对角线上。我们可以通过递归和回溯的方法解决该问题。具体思路如下:
- 使用一个递归函数进行逐行放置皇后。
- 在每次递归中尝试在当前行的每一列放置一个皇后。
- 创建三个记录数组来存储列和两条对角线的状态:
cols[i]
:表示第 i 列是否有皇后。d1[i + j]
:表示从 左下到右上的对角线是否有皇后(因为对角线的索引范围为 [-n+1, 2n-1],映射为 [0, 2n-1])。d2[i - j + n - 1]
:表示从左上到右下的对角线是否有皇后。
- 每当放置一个皇后时,更新这三个数组,然后递归地尝试在下一行放置皇后。
- 如果在第 n 行成功放置了 n 个皇后,则找到一种有效解决方案,计数加一。
- 如果探索完所有位置,回溯到前一行继续尝试其他位置。
代码实现
- Python
- C++
- JavaScript
- Java
def totalNQueens(n):
def backtrack(row):
# 使用 nonlocal 来修改外部作用域的变量 solutions
nonlocal solutions
if row == n:
# 找到一组有效解决方案
solutions += 1
return
for col in range(n):
if cols[col] or d1[row + col] or d2[row - col + n - 1]:
continue # 如果该列或两个对角线已被皇后占据,跳过
# 放置皇后
cols[col] = d1[row + col] = d2[row - col + n - 1] = True
backtrack(row + 1)
# 移除皇后
cols[col] = d1[row + col] = d2[row - col + n - 1] = False
solutions = 0
cols = [False] * n
d1 = [False] * (2 * n - 1)
d2 = [False] * (2 * n - 1)
backtrack(0)
return solutions
class Solution {
public:
int totalNQueens(int n) {
// 初始化控制变量
std::vector<bool> cols(n, false);
std::vector<bool> d1(2 * n - 1, false);
std::vector<bool> d2(2 * n - 1, false);
return backtrack(0, n, cols, d1, d2);
}
private:
int backtrack(int row, int n, std::vector<bool>& cols, std::vector<bool>& d1, std::vector<bool>& d2) {
if (row == n) {
// 找到一种有效解决方案
return 1;
}
int solutions = 0;
for (int col = 0; col < n; ++col) {
if (cols[col] || d1[row + col] || d2[row - col + n - 1])
continue; // 跳过已被占据的位置
// 放置皇后
cols[col] = d1[row + col] = d2[row - col + n - 1] = true;
solutions += backtrack(row + 1, n, cols, d1, d2);
// 移除皇后
cols[col] = d1[row + col] = d2[row - col + n - 1] = false;
}
return solutions;
}
};
function totalNQueens(n) {
let solutions = 0;
const cols = Array(n).fill(false);
const d1 = Array(2 * n - 1).fill(false);
const d2 = Array(2 * n - 1).fill(false);
function backtrack(row) {
if (row === n) {
solutions++;
return;
}
for (let col = 0; col < n; col++) {
if (cols[col] || d1[row + col] || d2[row - col + n - 1]) continue;
cols[col] = d1[row + col] = d2[row - col + n - 1] = true;
backtrack(row + 1);
cols[col] = d1[row + col] = d2[row - col + n - 1] = false;
}
}
backtrack(0);
return solutions;
}
class Solution {
public int totalNQueens(int n) {
boolean[] cols = new boolean[n];
boolean[] d1 = new boolean[2 * n - 1];
boolean[] d2 = new boolean[2 * n - 1];
return backtrack(0, n, cols, d1, d2);
}
private int backtrack(int row, int n, boolean[] cols, boolean[] d1, boolean[] d2) {
if (row == n) {
return 1;
}
int solutions = 0;
for (int col = 0; col < n; col++) {
if (cols[col] || d1[row + col] || d2[row - col + n - 1]) continue;
cols[col] = d1[row + col] = d2[row - col + n - 1] = true;
solutions += backtrack(row + 1, n, cols, d1, d2);
cols[col] = d1[row + col] = d2[row - col + n - 1] = false;
}
return solutions;
}
}
复杂度分析
时间复杂度:
在最坏情况下,递归树可能会产生多达 个节点,每个节点进行 的操作。
空间复杂度:
需要 的空间来存储列和对角线的状态,以及递归调用栈的深度为 。