DSA Notes
Solve the classic N-Queens problem using backtracking — learn constraint checking, state-space exploration, and trace through a complete 4-Queens walkthrough.
Problem Statement
Place N queens on an N×N chessboard such that no two queens attack each other. A queen attacks along its row, column, and both diagonals. For N=8, there are 92 distinct solutions — finding even one requires careful systematic search.
Building Intuition
Think of it this way: since no two queens can share a row, we must place exactly one queen per row. Our job is to decide *which column* each row's queen goes into. This transforms the problem from "place N queens anywhere on N² squares" into "assign a column to each of N rows," immediately reducing the search space from C(N²,N) to N^N, and with column-uniqueness to N!.
But N! is still large for N=8 (40,320). Backtracking prunes branches early — if placing a queen in row 3 immediately conflicts with a queen in row 1, we skip all configurations that extend from that partial placement.
Constraint Checking
Before placing a queen at position (row, col), we check three things against all previously placed queens at positions (r, c):
- Same column:
col == c— violated if any earlier queen is in the same column - Same main diagonal:
row - col == r - c— cells on the same\diagonal share the same (row - col) value - Same anti-diagonal:
row + col == r + c— cells on the same/diagonal share the same (row + col) value
We maintain three sets — columns, diag1, and diag2 — for O(1) conflict detection.
4-Queens Complete Walkthrough
Let us trace the algorithm on a 4×4 board (rows and columns numbered 0-3):
| Row 0: Try col 0 | Place Q at (0,0). cols={0}, d1={0}, d2={0} |
| Row 1: Try col 0 | CONFLICT (same col). Skip. |
| Try col 1 | CONFLICT (same diag: 1-1=0=0-0). Skip. |
| Try col 2 | OK. Place Q at (1,2). cols={0,2}, d1={0,-1}, d2={0,3} |
| Row 2: Try col 0 | CONFLICT (diag2: 2+0=2... no. diag1: 2-0=2... no. col 0 used). CONFLICT col. |
| Try col 1 | CONFLICT (diag2: 2+1=3, 3∈d2). Skip. |
| Try col 2 | CONFLICT (same col). Skip. |
| Try col 3 | CONFLICT (diag1: 2-3=-1, -1∈d1). Skip. |
| DEAD END | Backtrack, remove (1,2) |
| Try col 3 | OK. Place Q at (1,3). cols={0,3}, d1={0,-2}, d2={0,4} |
| Row 2: Try col 0 | CONFLICT (col 0 used). Skip. |
| Try col 1 | Check: col 1 free, d1=2-1=1 free, d2=2+1=3 free → OK! Place (2,1) |
| Row 3: Try col 0 | d1=3-0=3 free, d2=3+0=3... CONFLICT (3∈d2 from (1,3)? No, d2={0,4,3}). CONFLICT d2. |
| Try col 1 | CONFLICT (col 1). Skip. |
| Try col 2 | d1=3-2=1∈d1? d1={0,-2,1}. YES, CONFLICT. Skip. |
| Try col 3 | CONFLICT (col 3). Skip. |
| DEAD END | Backtrack |
| Try col 2 | d1=2-2=0∈d1? YES, CONFLICT. Skip. |
| Try col 3 | CONFLICT (col 3). Skip. |
| DEAD END | Backtrack, remove (1,3) |
| All columns exhausted for row 1 | Backtrack, remove (0,0) |
| Row 0: Try col 1 | Place Q at (0,1). cols={1}, d1={-1}, d2={1} |
| Row 1: Try col 0 | d1=1-0=1 free, d2=1+0=1 CONFLICT. Skip. |
| Try col 1 | CONFLICT col. Skip. |
| Try col 2 | d1=1-2=-1 CONFLICT. Skip. |
| Try col 3 | d1=1-3=-2 free, d2=1+3=4 free, col 3 free → OK! Place (1,3) |
| Row 2: Try col 0 | d1=2-0=2 free, d2=2+0=2 free, col 0 free → OK! Place (2,0) |
| Row 3: Try col 2 | d1=3-2=1 free, d2=3+2=5 free, col 2 free → OK! Place (3,2) |
| ✓ SOLUTION FOUND | [1, 3, 0, 2] |
The solution [1, 3, 0, 2] means: row 0 → col 1, row 1 → col 3, row 2 → col 0, row 3 → col 2.
Python Implementation
C++ Implementation
Complexity Analysis
| Metric | Value | Explanation |
|---|---|---|
| Time complexity | O(N!) | At most N choices for row 0, N-1 for row 1, etc. Pruning reduces this significantly. |
| Space complexity | O(N) | Recursion depth is N; three sets store at most N entries each |
| Solutions for N=8 | 92 | Nodes explored ≈ 15,000 (vs 40,320 without diagonal pruning) |
Optimization: Bitmask Approach
For competitive programming, bitmasking avoids hash set overhead:
def n_queens_bitmask(n):
count = 0
def solve(row, cols, d1, d2):
nonlocal count
if row == n:
count += 1
return
available = ((1 << n) - 1) & ~(cols | d1 | d2)
while available:
pos = available & (-available) # lowest set bit
available ^= pos
solve(row + 1, cols | pos, (d1 | pos) << 1, (d2 | pos) >> 1)
solve(0, 0, 0, 0)
return count
print(n_queens_bitmask(12)) # 14200, runs very fastReal-World Applications
- VLSI design: Placing components without interference follows similar constraint logic
- Parallel computing: Task scheduling where processes cannot share certain resources
- Telecommunications: Frequency assignment to avoid interference
- Testing and QA: Generating non-conflicting test configurations
Key Takeaways
The N-Queens problem beautifully illustrates how backtracking transforms an exponential brute-force search into a manageable computation. The keys are: reduce each decision to one row at a time, check constraints in O(1) using sets or bitmasks, and backtrack immediately upon conflict. This pattern of "constrained placement with undo" appears in dozens of interview problems.
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for N-Queens Problem.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this Data Structures & Algorithms topic.
Search Terms
data-structures-algorithms, data structures & algorithms, data, structures, algorithms, backtracking, queens, problem
Related Data Structures & Algorithms Topics