The N-Queens II problem asks you to determine the total number of distinct ways to place n
queens on an n x n
chessboard so that no two queens attack each other. A queen can attack horizontally, vertically, and diagonally. Your task is to return the total count of valid arrangements, not the arrangements themselves.
n
(where 1 ≤ n ≤ 9
).
At first glance, you might consider brute-forcing every possible way to place n
queens on the board and checking if each configuration is valid. However, since the number of possible placements grows rapidly with n
, this approach is not practical for larger boards.
Instead, we can use recursion and backtracking to build solutions step by step. The key insight is that by placing one queen per row and ensuring that no two queens threaten each other, we can reduce the search space significantly. We can also use sets or arrays to efficiently track which columns and diagonals are already under attack, allowing us to quickly determine if a position is safe for placing a queen.
The shift from brute-force to backtracking is crucial: instead of generating all board states, we only try positions that are guaranteed not to violate the constraints up to that point.
We use a recursive backtracking algorithm to solve the N-Queens II problem. Here’s how the solution works step by step:
row - col
are on the same main diagonal.row + col
are on the same anti-diagonal.n
, it means we’ve placed all queens successfully, so we increment our solution count.This approach is efficient because it prunes invalid branches early, never exploring board configurations that would result in conflicts later.
Let’s walk through the process for n = 4
:
n=4
. The algorithm counts both and returns 2.
At every step, invalid configurations are pruned immediately, so the algorithm never tries to place queens in positions that would lead to conflicts.
class Solution:
def totalNQueens(self, n: int) -> int:
def backtrack(row, cols, diag1, diag2):
if row == n:
return 1
count = 0
for col in range(n):
if col in cols or (row - col) in diag1 or (row + col) in diag2:
continue
cols.add(col)
diag1.add(row - col)
diag2.add(row + col)
count += backtrack(row + 1, cols, diag1, diag2)
cols.remove(col)
diag1.remove(row - col)
diag2.remove(row + col)
return count
return backtrack(0, set(), set(), set())
class Solution {
public:
int totalNQueens(int n) {
return backtrack(0, n, vector<bool>(n), vector<bool>(2 * n), vector<bool>(2 * n));
}
private:
int backtrack(int row, int n, vector<bool>& cols, vector<bool>& diag1, vector<bool>& diag2) {
if (row == n) return 1;
int count = 0;
for (int col = 0; col < n; ++col) {
int d1 = row - col + n;
int d2 = row + col;
if (cols[col] || diag1[d1] || diag2[d2]) continue;
cols[col] = diag1[d1] = diag2[d2] = true;
count += backtrack(row + 1, n, cols, diag1, diag2);
cols[col] = diag1[d1] = diag2[d2] = false;
}
return count;
}
};
class Solution {
public int totalNQueens(int n) {
return backtrack(0, n, new boolean[n], new boolean[2 * n], new boolean[2 * n]);
}
private int backtrack(int row, int n, boolean[] cols, boolean[] diag1, boolean[] diag2) {
if (row == n) return 1;
int count = 0;
for (int col = 0; col < n; col++) {
int d1 = row - col + n;
int d2 = row + col;
if (cols[col] || diag1[d1] || diag2[d2]) continue;
cols[col] = diag1[d1] = diag2[d2] = true;
count += backtrack(row + 1, n, cols, diag1, diag2);
cols[col] = diag1[d1] = diag2[d2] = false;
}
return count;
}
}
var totalNQueens = function(n) {
function backtrack(row, cols, diag1, diag2) {
if (row === n) return 1;
let count = 0;
for (let col = 0; col < n; col++) {
if (cols.has(col) || diag1.has(row - col) || diag2.has(row + col)) continue;
cols.add(col);
diag1.add(row - col);
diag2.add(row + col);
count += backtrack(row + 1, cols, diag1, diag2);
cols.delete(col);
diag1.delete(row - col);
diag2.delete(row + col);
}
return count;
}
return backtrack(0, new Set(), new Set(), new Set());
};
n^n
ways to place n
queens on an n x n
board (since each queen can go in any square), but most are invalid. Checking each configuration for validity is also expensive, leading to an exponential time complexity.
O(n!)
because for each row, there is at most one queen per column, and we never revisit columns. For small n
(up to 9), this is efficient.
O(n)
for the recursion stack and for the sets/arrays tracking columns and diagonals.
The optimized backtracking approach is efficient for the problem constraints.
The N-Queens II problem challenges us to count all valid ways to place n
queens on an n x n
chessboard without conflict. By using backtracking and efficiently tracking columns and diagonals, we prune invalid placements early and count only valid solutions. This approach is both elegant and practical for the given constraints, and highlights the power of recursive problem-solving with state tracking.