🧠
Backtracking with Bitmask Optimization
💡 Using bitmasks to represent columns and diagonals reduces memory and speeds up conflict checks, enabling very fast pruning.
Intuition
Represent columns and diagonals as bits in integers; use bitwise operations to quickly check and update attacked positions.
Algorithm
- Use three integers to track columns, positive diagonals, and negative diagonals under attack.
- At each row, compute available positions by bitwise negation and AND with mask.
- Iterate over each available position by extracting the rightmost set bit.
- Place queen by updating bitmasks and recurse to next row.
- Backtrack by restoring bitmasks; count solutions when all rows are placed.
💡 Bitmasking allows constant-time conflict checks and efficient iteration over valid positions, drastically reducing overhead.
class Solution:
def totalNQueens(self, n: int) -> int:
def backtrack(row, cols, pos_diags, neg_diags):
if row == n:
return 1
count = 0
available_positions = ((1 << n) - 1) & ~(cols | pos_diags | neg_diags)
while available_positions:
position = available_positions & -available_positions # rightmost 1-bit
available_positions &= available_positions - 1 # remove rightmost 1-bit
count += backtrack(row + 1,
cols | position,
(pos_diags | position) << 1,
(neg_diags | position) >> 1)
return count
return backtrack(0, 0, 0, 0)
# Driver code
if __name__ == '__main__':
sol = Solution()
print(sol.totalNQueens(4)) # Output: 2
Line Notes
available_positions = ((1 << n) - 1) & ~(cols | pos_diags | neg_diags)Calculate all free columns for current row
position = available_positions & -available_positionsExtract rightmost available position to try
available_positions &= available_positions - 1Remove the tried position from available positions
count += backtrack(row + 1, cols | position, (pos_diags | position) << 1, (neg_diags | position) >> 1)Recurse with updated attacked positions
if row == n:Base case: all queens placed
return backtrack(0, 0, 0, 0)Start recursion with empty board
public class Solution {
private int count = 0;
public int totalNQueens(int n) {
backtrack(n, 0, 0, 0, 0);
return count;
}
private void backtrack(int n, int row, int cols, int posDiags, int negDiags) {
if (row == n) {
count++;
return;
}
int availablePositions = ((1 << n) - 1) & ~(cols | posDiags | negDiags);
while (availablePositions != 0) {
int position = availablePositions & (-availablePositions);
availablePositions &= availablePositions - 1;
backtrack(n, row + 1, cols | position, (posDiags | position) << 1, (negDiags | position) >> 1);
}
}
// Driver code
public static void main(String[] args) {
Solution sol = new Solution();
System.out.println(sol.totalNQueens(4)); // Output: 2
}
}
Line Notes
int availablePositions = ((1 << n) - 1) & ~(cols | posDiags | negDiags);Calculate free columns for current row
int position = availablePositions & (-availablePositions);Extract rightmost available position
availablePositions &= availablePositions - 1;Remove tried position
backtrack(n, row + 1, cols | position, (posDiags | position) << 1, (negDiags | position) >> 1);Recurse with updated attacked positions
if (row == n)Base case: all queens placed
count++Increment solution count
#include <iostream>
using namespace std;
class Solution {
public:
int count = 0;
int totalNQueens(int n) {
backtrack(n, 0, 0, 0, 0);
return count;
}
private:
void backtrack(int n, int row, int cols, int posDiags, int negDiags) {
if (row == n) {
count++;
return;
}
int availablePositions = ((1 << n) - 1) & ~(cols | posDiags | negDiags);
while (availablePositions) {
int position = availablePositions & (-availablePositions);
availablePositions &= availablePositions - 1;
backtrack(n, row + 1, cols | position, (posDiags | position) << 1, (negDiags | position) >> 1);
}
}
};
int main() {
Solution sol;
cout << sol.totalNQueens(4) << endl; // Output: 2
return 0;
}
Line Notes
int availablePositions = ((1 << n) - 1) & ~(cols | posDiags | negDiags);Calculate free columns for current row
int position = availablePositions & (-availablePositions);Extract rightmost available position
availablePositions &= availablePositions - 1;Remove tried position
backtrack(n, row + 1, cols | position, (posDiags | position) << 1, (negDiags | position) >> 1);Recurse with updated attacked positions
if (row == n)Base case: all queens placed
count++Increment solution count
var totalNQueens = function(n) {
let count = 0;
function backtrack(row, cols, posDiags, negDiags) {
if (row === n) {
count++;
return;
}
let availablePositions = ((1 << n) - 1) & ~(cols | posDiags | negDiags);
while (availablePositions) {
let position = availablePositions & (-availablePositions);
availablePositions &= availablePositions - 1;
backtrack(row + 1, cols | position, (posDiags | position) << 1, (negDiags | position) >> 1);
}
}
backtrack(0, 0, 0, 0);
return count;
};
// Driver code
console.log(totalNQueens(4)); // Output: 2
Line Notes
let availablePositions = ((1 << n) - 1) & ~(cols | posDiags | negDiags);Calculate free columns for current row
let position = availablePositions & (-availablePositions);Extract rightmost available position
availablePositions &= availablePositions - 1;Remove tried position
backtrack(row + 1, cols | position, (posDiags | position) << 1, (negDiags | position) >> 1);Recurse with updated attacked positions
if (row === n)Base case: all queens placed
count++Increment solution count
Bitmasking reduces overhead of conflict checks to O(1), but the search space remains factorial.
💡 This approach is the fastest practical solution for n up to 14, the problem's upper limit.
Interview Verdict: Accepted
This is the optimal approach for this problem and is often expected in high-level interviews.