N-Queens¶
Place \(n\) chess queens on an \(n \times n\) board so that no two attack each other (no two share a row, a column or a diagonal). Backtracking places one queen per row and steps back as soon as a row has no safe square.
| Time | Extra space | Output |
|---|---|---|
| exponential, but heavily pruned (2,057 partial boards for \(n = 8\)) | \(O(n)\) | the number of solutions and the first one |
The idea¶
A queen attacks along its row, its column and both diagonals. Since no two queens may share a row, there is exactly one queen per row. So: place a queen in row 0, then find a safe column in row 1, then in row 2, and so on. If some row has no safe column, the earlier choices were wrong: back up to the previous row and move its queen to the next column.
To check "is this square safe?" in \(O(1)\), keep three lists of flags:
cols[c]: column \(c\) has a queen;diag1[r + c]: the "/" diagonal through \((r, c)\) has a queen (along it, \(r + c\) is constant);diag2[r - c + n]: the "\" diagonal has a queen (along it, \(r - c\) is constant; \(+n\) keeps the index positive).
How it works¶
go(r): if \(r = n\), all queens are placed: count the solution.- Otherwise, for each column \(c\): if column \(c\) and both diagonals are free, mark them, record the queen,
call
go(r + 1), then clear the marks.
Code¶
def n_queens(n):
"""Place n queens on an n x n board so that none attacks another. One queen per row: try each column,
skip columns and diagonals already under attack, and back up when a row has no safe square.
Returns the number of solutions and the first one found (queen's column in each row)."""
cols, diag1, diag2 = [False] * n, [False] * (2 * n), [False] * (2 * n)
place, first, count = [0] * n, [], 0
def go(r):
nonlocal count, first
if r == n:
count += 1
if not first:
first = list(place)
return
for c in range(n):
if cols[c] or diag1[r + c] or diag2[r - c + n]: # attacked: prune this branch
continue
cols[c] = diag1[r + c] = diag2[r - c + n] = True
place[r] = c
go(r + 1)
cols[c] = diag1[r + c] = diag2[r - c + n] = False # undo, try the next column
go(0)
return count, first
if __name__ == "__main__":
print([n_queens(n)[0] for n in range(1, 11)])
print(n_queens(8)[1])
for c in n_queens(6)[1]:
print("".join("Q" if k == c else "." for k in range(6)))
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class NQueens {
static int n, count;
static boolean[] cols, diag1, diag2;
static int[] place, first;
/** Place n queens on an n x n board so that none attacks another. One queen per row: try each column,
* skip columns and diagonals already under attack, and back up when a row has no safe square.
* Returns the number of solutions; the first one found is left in first. */
static int nQueens(int size) {
n = size; count = 0; first = null;
cols = new boolean[n]; diag1 = new boolean[2 * n]; diag2 = new boolean[2 * n];
place = new int[n];
go(0);
return count;
}
static void go(int r) {
if (r == n) {
count++;
if (first == null) first = place.clone();
return;
}
for (int c = 0; c < n; c++) {
if (cols[c] || diag1[r + c] || diag2[r - c + n]) continue; // attacked: prune this branch
cols[c] = diag1[r + c] = diag2[r - c + n] = true;
place[r] = c;
go(r + 1);
cols[c] = diag1[r + c] = diag2[r - c + n] = false; // undo, try the next column
}
}
public static void main(String[] args) {
List<Integer> counts = new ArrayList<>();
for (int k = 1; k <= 10; k++) counts.add(nQueens(k));
System.out.println(counts);
nQueens(8);
System.out.println(Arrays.toString(first));
nQueens(6);
for (int c : first) {
StringBuilder row = new StringBuilder();
for (int k = 0; k < 6; k++) row.append(k == c ? 'Q' : '.');
System.out.println(row);
}
}
}
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int n, countSolutions;
vector<bool> cols, diag1, diag2;
vector<int> place, first;
void go(int r) {
if (r == n) {
countSolutions++;
if (first.empty()) first = place;
return;
}
for (int c = 0; c < n; c++) {
if (cols[c] || diag1[r + c] || diag2[r - c + n]) continue; // attacked: prune this branch
cols[c] = diag1[r + c] = diag2[r - c + n] = true;
place[r] = c;
go(r + 1);
cols[c] = diag1[r + c] = diag2[r - c + n] = false; // undo, try the next column
}
}
// Place n queens on an n x n board so that none attacks another. One queen per row: try each column,
// skip columns and diagonals already under attack, and back up when a row has no safe square.
// Returns the number of solutions; the first one found is put in first.
int nQueens(int size) {
n = size; countSolutions = 0; first.clear();
cols.assign(n, false); diag1.assign(2 * n, false); diag2.assign(2 * n, false);
place.assign(n, 0);
go(0);
return countSolutions;
}
int main() {
cout << "[";
for (int k = 1; k <= 10; k++) cout << (k > 1 ? ", " : "") << nQueens(k);
cout << "]\n";
nQueens(8);
cout << "[";
for (int i = 0; i < 8; i++) cout << (i ? ", " : "") << first[i];
cout << "]\n";
nQueens(6);
for (int c : first) {
string row(6, '.');
row[c] = 'Q';
cout << row << "\n";
}
}
Output: the number of solutions for \(n = 1\) to 10, [1, 0, 0, 2, 10, 4, 40, 92, 352, 724]; the first solution
for 8 queens, [0, 4, 7, 5, 2, 6, 1, 3] (row 0 has its queen in column 0, row 1 in column 4, ...); and the first
6-queens board:
.Q....
...Q..
.....Q
Q.....
..Q...
....Q.
When to use it¶
- Constraint puzzles: Sudoku, crosswords, map colouring, timetables and seating plans have the same shape: fill slots one by one, check the rules at once, back up on a dead end.
- As a benchmark: N-Queens is the standard example for testing search and pruning ideas.
- When you need one solution for large \(n\): backtracking finds one quickly up to a few dozen; for huge \(n\) there are direct formulas or local search (see Variations).
Common mistakes¶
- Checking the whole board for every square: \(O(n)\) or \(O(n^2)\) per check instead of \(O(1)\) with the flag lists.
- Wrong diagonal index: \(r - c\) can be negative; shift it by \(n\).
- Forgetting to clear the flags after the recursive call.
- Expecting solutions for \(n = 2\) and 3: there are none.
Going deeper¶
Each part below starts closed; open the ones you want.
Sample walkthrough: 4 queens
Columns are numbered 0–3.
- Row 0: queen in column 0.
- Row 1: columns 0 and 1 are attacked; column 2 is safe. Row 2: every column is attacked, so back up.
- Row 1: column 3. Row 2: column 0 attacked, column 1 safe. Row 3: every column attacked, so back up.
- Row 2: columns 2 and 3 attacked. Row 1 has no columns left, so back up to row 0.
- Row 0: column 1. Row 1: column 3. Row 2: column 0. Row 3: column 2. Solution
[1, 3, 0, 2]:
.Q..
...Q
Q...
..Q.
The other 4-queens solution is its mirror image, [2, 0, 3, 1].
The math: how much pruning helps, and how many solutions there are
Search space.
| Approach | Boards looked at for \(n = 8\) |
|---|---|
| any 8 of the 64 squares | \(\binom{64}{8} = 4{,}426{,}165{,}368\) |
| one queen per row | \(8^8 = 16{,}777{,}216\) |
| one per row and column (permutations) | \(8! = 40{,}320\) |
| backtracking with early checks (this code) | 2,057 partial boards (nodes of the search tree) |
Number of solutions \(Q(n)\): 1, 0, 0, 2, 10, 4, 40, 92, 352, 724, ... (OEIS A000170). The 92 solutions for \(n = 8\) come in groups related by rotating and reflecting the board; there are 12 essentially different ones. No simple formula is known; \(Q(27)\) was computed in 2016 with a large distributed search. Simkin (2021) showed that \(Q(n) = \left((1 + o(1))\, n e^{-\alpha}\right)^n\) with \(\alpha \approx 1.944\).
A solution always exists for \(n \ge 4\), and one can be written down directly (constructions going back to the 19th century), so finding one solution is easy even for huge \(n\). Counting all of them is the hard part.
Watch it step by step
Green: queens placed. Faded blue: squares attacked by them in later rows. Red ✕: the square just tried and found attacked. Purple: the finished solution.
Variations and related ideas
- Bitmasks: keep the three flag lists as bits of three integers; the free columns of a row are
~(cols | d1 | d2) & ((1 << n) - 1); this is the fastest known way to count solutions. - Symmetry: only try the first queen in the left half of row 0 and double the count (care with the middle column when \(n\) is odd).
- Min-conflicts local search: start with any placement and move queens to reduce attacks; solves a million queens in seconds.
- Exact cover: N-Queens, Sudoku and pentomino puzzles can all be phrased as exact cover and solved with Knuth's Algorithm X ("dancing links").
See also¶
- Subsets and permutations
- Depth-first search: backtracking is a DFS of the decision tree
- Backtracking
References¶
- N. Wirth, Algorithms + Data Structures = Programs, Prentice-Hall, 1976, Section 3.5 (the eight queens problem).
- J. Bell and B. Stevens, "A survey of known results and research areas for n-queens", Discrete Mathematics 309(1), 2009.
- M. Simkin, "The number of n-queens configurations", Advances in Mathematics 427, 2023.
- D. E. Knuth, "Dancing links", in Millennial Perspectives in Computer Science, 2000.