N-Queens II
Count the n-queens solutions using bitmask backtracking over columns, diagonals, and anti-diagonals.
4 min read
backtracking bitmask
Problem#
Return the number of distinct ways to place n queens on an n × n board such that no two attack each other.
Examples#
n = 4→2n = 1→1
Constraints#
1 <= n <= 9.
Approach#
Bitmask backtracking. Three bitmasks track occupied columns, anti-diagonals (/), and diagonals (\). At each row, available positions = ~(cols | diag | anti) & ((1<<n) - 1). Pick the lowest set bit, recurse, clear.
Solution#
class Solution {public: int totalNQueens(int n) { int count = 0; function<void(int,int,int,int)> dfs = [&](int row, int cols, int diag, int anti) { if (row == n) { ++count; return; } int avail = ~(cols | diag | anti) & ((1 << n) - 1); while (avail) { int bit = avail & -avail; // lowest set bit avail ^= bit; dfs(row + 1, cols | bit, (diag | bit) << 1, (anti | bit) >> 1); } }; dfs(0, 0, 0, 0); return count; }};func totalNQueens(n int) int { count := 0 var dfs func(row, cols, diag, anti int) dfs = func(row, cols, diag, anti int) { if row == n { count++ return } avail := ^(cols | diag | anti) & ((1 << n) - 1) for avail != 0 { bit := avail & -avail avail ^= bit dfs(row+1, cols|bit, (diag|bit)<<1, (anti|bit)>>1) } } dfs(0, 0, 0, 0) return count}class Solution: def totalNQueens(self, n: int) -> int: count = 0 full = (1 << n) - 1
def dfs(row: int, cols: int, diag: int, anti: int) -> None: nonlocal count if row == n: count += 1 return avail = ~(cols | diag | anti) & full while avail: bit = avail & -avail avail ^= bit dfs(row + 1, cols | bit, ((diag | bit) << 1) & full, (anti | bit) >> 1)
dfs(0, 0, 0, 0) return countfunction totalNQueens(n) { let count = 0; const full = (1 << n) - 1; const dfs = (row, cols, diag, anti) => { if (row === n) { count++; return; } let avail = ~(cols | diag | anti) & full; while (avail) { const bit = avail & -avail; avail ^= bit; dfs(row + 1, cols | bit, ((diag | bit) << 1) & full, (anti | bit) >> 1); } }; dfs(0, 0, 0, 0); return count;}class Solution { private int count; private int n; private int full;
public int totalNQueens(int n) { this.n = n; this.full = (1 << n) - 1; this.count = 0; dfs(0, 0, 0, 0); return count; }
private void dfs(int row, int cols, int diag, int anti) { if (row == n) { count++; return; } int avail = ~(cols | diag | anti) & full; while (avail != 0) { int bit = avail & -avail; avail ^= bit; dfs(row + 1, cols | bit, ((diag | bit) << 1) & full, (anti | bit) >> 1); } }}function totalNQueens(n: number): number { let count = 0; const full = (1 << n) - 1; const dfs = (row: number, cols: number, diag: number, anti: number): void => { if (row === n) { count++; return; } let avail = ~(cols | diag | anti) & full; while (avail) { const bit = avail & -avail; avail ^= bit; dfs(row + 1, cols | bit, ((diag | bit) << 1) & full, (anti | bit) >> 1); } }; dfs(0, 0, 0, 0); return count;}Editorial#
Shifting the diagonal/anti-diagonal masks on recursion accounts for their diagonal progression — \ moves right-and-down (shift left), / moves left-and-down (shift right). The lowest-set-bit bit & -bit trick iterates exactly the available columns, skipping blocked ones.
Complexity#
- Time: O(n!).
- Space: O(n).
Concept revision#
Related#