N-Queens
The classic backtracking problem: place n queens so none attacks another, backing up at dead ends.
Worth knowing first
#How does it work?
Backtracking builds a solution step by step and, as soon as a step can't lead to a solution, undoes it and tries another option. Instead of blindly trying every possibility, it prunes hopeless branches early.
The N-Queens problem is the classic example: place n queens on an n × n chessboard so that none attacks another. A queen attacks every piece on its row, column and diagonals.
Since every row needs exactly one queen, we go row by row. In each row we put a queen on the first column no queen above attacks and move to the next row. If a row has no safe column left, the queen in the row above is removed and its next column is tried.
An 8 × 8 board has 92 solutions. There are over 4 billion ways to drop 8 queens on 64 squares, yet backtracking tries only a few thousand squares.
#Try it yourself
Current choice
· · · ·Place n queens on an n × n board so that none attacks another: no two may share a column or a diagonal. We put one queen per row and back up at dead ends.
#Step by step
- 01Start at the first row.
- 02For each column in the row: skip it if a queen above attacks the square.
- 03If it is safe, place a queen and move to the next row; if the last row is filled, a solution is found.
- 04If a row has no safe square, go back: remove the queen in the row above and try its next column.
#Example code
The same algorithm in 8 languages. Every program is compiled, run and checked to print the same output.
def solve(n: int):
"""First solution as a list of columns (one per row), or None."""
queens = [] # queens[row] = column
def safe(row: int, col: int) -> bool:
return all(c != col and abs(c - col) != row - r for r, c in enumerate(queens))
def place(row: int) -> bool:
if row == n:
return True
for col in range(n):
if safe(row, col):
queens.append(col)
if place(row + 1):
return True
queens.pop() # dead end: backtrack
return False
return queens if place(0) else None
def count(n: int) -> int:
"""All solutions, with sets of used columns and diagonals for O(1) checks."""
cols, diag1, diag2 = set(), set(), set()
def place(row: int) -> int:
if row == n:
return 1
total = 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)
total += place(row + 1)
cols.remove(col)
diag1.remove(row - col)
diag2.remove(row + col)
return total
return place(0)
print(" ".join(str(c + 1) for c in solve(4))) # 1-based columns by row
print(count(8))#Complexity
| Operation | Time |
|---|---|
| Worst case (no pruning) | O(n!) |
| Checking one square | O(n), O(1) with column/diagonal sets |
| Memory | O(n) |
| Squares tried for 8 × 8 | a few thousand |
One queen per row shrinks the search from nⁿ to n!, and pruning with the attack check cuts it far further. With three boolean arrays for columns and both diagonal directions, each check is O(1). With bit masks, all solutions can be counted quickly up to about n = 15.
#When to use it?
Backtracking is the general pattern for "find a placement that satisfies the constraints" problems: Sudoku, puzzles, maze solving, constraint satisfaction (timetables, exam schedules), graph colouring and generating combinations.
#Practice
Log in to answer questions and save your progress.
Easy
What does backtracking do at a dead end?
- 1Undoes the last choice and tries another option
- 2Starts over from scratch
- 3Makes a random choice
- 4Stops and reports no solution
When do two queens attack each other?
- 1When they share a row, column or diagonal
- 2Only when they share a row
- 3When they are on adjacent squares
- 4When they are on squares of the same colour
How many solutions does the standard 8 × 8 board have?
- 192
- 28
- 364
- 412
Medium
Which condition says queens at (r, c) and (r2, c2) share a diagonal?
- 1|c − c2| = |r − r2|
- 2c + c2 = r + r2
- 3c = c2
- 4r · c = r2 · c2
Hard
How does putting exactly one queen per row shrink the search?
- 1From choosing among n² squares down to n! or fewer, and pruning cuts it further
- 2It doesn't change it
- 3It halves the search
- 4It only reduces memory use
#Up next
Lessons that build on this one: