N-Queens
Place n queens on an n × n chessboard so that no two attack each other, and return every distinct arrangement.
A queen attacks along its entire row, its entire column, and both diagonals, without limit of distance. So a valid board has at most one queen in any row, any column, and any diagonal.
Each solution is returned as n strings of length n, using 'Q' for a queen and '.' for an empty square. Solutions may be returned in any order, and an empty list is correct when no arrangement exists.
- 1 <= n <= 9
- Exactly n queens must be placed
- No two queens may share a row, a column, or either diagonal
- Diagonals extend the full length of the board, not just adjacent squares
- n = 2 and n = 3 have no solutions at all
n = 42 solutions: [".Q..","...Q","Q...","..Q."] and ["..Q.","Q...","...Q",".Q.."]n = 11 solution: ["Q"]n = 2[] — no solutionsn = 3[] — no solutionsPlace n queens so none attack each other. Key reframe: there are n queens and n rows, so exactly one queen per row. That means the only choice per row is which column. Picking a column per row makes the row rule impossible to break for free — now only columns and the two diagonals need checking.
Diagonals have a neat fingerprint: a ↘ diagonal has constant row - col; a ↗ diagonal has constant row + col. Keep a set of used columns and used diagonals, and a safety check is just three lookups.
Every problem in this section is the same loop — three beats: choose, explore, un-choose.
def backtrack(state):
if done(state):
save(state) # record the finished answer
return
for choice in choices(state): # what can I pick right now?
if not ok(choice):
continue # prune: skip bad picks early
apply(choice) # CHOOSE
backtrack(next_state) # EXPLORE
undo(choice) # UN-CHOOSE: put it backOnly four slots change between problems: done, choices, ok (pruning), and how you finish (save every answer, or return True at the first one). Fill those and the problem is solved.
col.0..n-1.row == n) → save the board.col if its column, ↘ diagonal, or ↗ diagonal is taken.def solve_n_queens(n):
res, place = [], []
cols, diag, anti = set(), set(), set() # columns, row-col, row+col
def backtrack(row):
if row == n:
res.append(["." * c + "Q" + "." * (n-c-1) for c in place])
return
for col in range(n):
if col in cols or (row-col) in diag or (row+col) in anti:
continue # PRUNE: attacked
cols.add(col); diag.add(row-col); anti.add(row+col) # CHOOSE
place.append(col)
backtrack(row + 1) # EXPLORE
place.pop() # UN-CHOOSE
cols.remove(col); diag.remove(row-col); anti.remove(row+col)
backtrack(0)
return resUndo all the state (column + both diagonals + place) — miss one and the count silently comes out wrong. One queen per row is automatic because we always recurse to row + 1.
Trace n=4: row0 col1 → row1 col3 → row2 col0 → row3 col2 gives [1,3,0,2]; the mirror gives [2,0,3,1]. Two solutions.
N-Queens (Recursive DFS)
MINDSET
We place queens row-by-row. At each cell, we verify vertical and diagonal constraints. If a row has no valid spot, the entire branch is invalid.
PRUNING
A brute-force search of all queen positions would be N^(2N). Backtracking row-by-row with safety checks reduces this to O(N!) and even less in practice.