ALGORITHMS · 17  /  N-QUEENS

The Standoff.

Put four queens on a board so no two can attack — no shared row, column, or diagonal. There's no formula, only search: place a queen, try to build on it, and when you hit a dead end, undo and try again. That undo is backtracking.

THE GIST · 20 SECONDS

N-Queens places one queen per column, left to right. In each column, try each row: a row is safe if no earlier queen shares its row or a diagonal. On a safe square, place and recurse to the next column. If a column has no safe row, backtrack — pull the previous queen and try its next spot. Fill all N columns and you've solved it.

  • Backtrackundo a bad choice
  • Safeno row / diagonal clash
  • Recursesolve the next column
  • Pruneabandon dead branches

Hit Step to try the next square — or Play and watch the search place, clash, backtrack, and finally solve it.

UNDER THE HOOD

What you just played, written down

Backtracking is depth-first search with an undo button. Make a choice, dive deeper, and the moment a partial answer can't work, rewind to the last decision and take the other road.

How backtracking thinks — four moves

  1. One choice at a time. Fill columns left to right; each column needs exactly one queen, in some row.
  2. Check before you commit. A row is safe only if no placed queen shares that row or either diagonal.
  3. Place & recurse. On a safe square, place the queen and solve the next column. Success there means success here.
  4. Dead end? Undo. If no row in a column is safe, remove the previous queen and try its next row — backtrack.
WHY UNDOING IS THE WHOLE IDEA

A wrong early queen poisons every arrangement built on top of it. Rather than test them all, backtracking abandons the whole branch the instant it's doomed — then quietly restores the board and tries the next option.

The whole thing in code

def solve(n):
    pos = [-1] * n          # pos[col] = row of its queen

    def safe(col, row):
        for c in range(col):
            r = pos[c]
            if r == row or abs(r - row) == abs(c - col):
                return False     # same row or diagonal
        return True

    def place(col):
        if col == n:
            return True          # all columns filled - solved
        for row in range(n):
            if safe(col, row):
                pos[col] = row   # choose
                if place(col + 1):
                    return True  # recurse
                pos[col] = -1    # UNDO - backtrack
        return False

    return pos if place(0) else None
TIMEO(N!)worst case, heavily pruned
SPACEO(N)recursion + board
PRUNING BEATS BRUTE FORCE

Checking safety before recursing means a doomed placement never spawns its subtree. That early failure is what makes backtracking search the whole space without visiting the whole space.

⚠ Still exponential

Pruning helps enormously but the worst case is super-polynomial. For large N you add smarter heuristics (most-constrained column first) or switch to specialised methods; backtracking is general, not always fast.

↔ One queen per column, by design

Two queens in one column would attack instantly, so we bake that constraint in — one per column — and only ever search over rows. Fewer choices, faster search.

★ Where it's used

Sudoku and constraint puzzles, permutations/combinations, subset-sum, maze solving, graph coloring, and parsing — anywhere you build incrementally and can detect a dead end early.

SOLVE IT YOURSELF

Solve it: count N-Queens solutions

This one is yours to write — in Python or TypeScript, running for real in your browser. Backtrack column by column and count every full solution. Edit the stub, hit Run (or ⌘/Ctrl + Enter), and watch the hidden tests. Stuck? the hints are below and Reveal solution is one click away.

YOUR TASK

Implement count_queens(n): return how many distinct ways N queens can be placed on an N×N board so no two attack each other. Use backtracking — place one queen per column and count the completed boards.

HINTS — 4 IDEAS
  1. Track pos[col] = row for placed queens; place one column at a time.
  2. safe(col, row): no earlier queen shares the row (r == row) or a diagonal (abs(r-row) == abs(c-col)).
  3. When col == n, you've filled every column — that's one solution, add 1.
  4. After recursing, undo the choice (or just don't persist it) and try the next row.
CPython · WebAssembly
QUICK CHECK

Did it stick?

FAQ

N-Queens, answered

What is the N-Queens problem?

Place N queens on an N×N board so no two attack — no shared row, column, or diagonal. It's the classic puzzle used to teach backtracking.

What is backtracking?

Depth-first search with an undo: build a solution one choice at a time, and abandon a partial candidate the moment it can't be completed — then rewind and try the next option.

How does it solve N-Queens?

Place one queen per column, left to right. Try each row; if safe, place and recurse to the next column; if a column has no safe row, remove the previous queen and try its next row.

Why is it faster than brute force?

It prunes: a doomed partial placement never spawns its subtree, so huge regions of arrangements are skipped. The worst case is still exponential, but far fewer boards are actually visited.

Where else is backtracking used?

Sudoku, permutations/combinations, subset-sum, maze solving, graph coloring, regex matching, and parsing — anywhere you build incrementally and can spot a dead end early.

RESULT

Next → the exact same try-recurse-undo skeleton solves Sudoku, generates every permutation, and colors graphs. Learn backtracking once, reuse it everywhere.

Finished this one? 0 / 17 Algorithms done

Explore the topic

See this alongside everything else on the same subject — handbooks, system designs, challenges and tools, in one place.

More Algorithms