79.N-Queens
Problem
Place n queens on an n x n chessboard so that no two queens attack each other — no two share a row, a column, or a diagonal. Return all distinct solutions.
Each solution is a board drawn as a list of n strings, where 'Q' marks a queen and '.' an empty square.
Examples
Input: n = 4
Output: [[".Q..","...Q","Q...","..Q."],
["..Q.","Q...","...Q",".Q.."]]
Explanation: There are exactly two ways to place 4 non-attacking queens.Input: n = 1
Output: [["Q"]]Constraints
1 <= n <= 9
💡 Key Insight
Each row holds exactly one queen, so place queens row by row and only choose a column. A square (r, c) is attacked if its column, its main diagonal (r - c constant) or its anti-diagonal (r + c constant) is already taken. Tracking those three sets turns each safety check from an O(n) board scan into O(1); packing them into bitmasks makes it even faster and lets you enumerate free columns directly.
Backtracking with Board Scan
TimeO(n! · n)SpaceO(n²)Intuition
Put a queen in row r, column c only if no earlier queen attacks it. To check, scan upward in the same column and along both upward diagonals (rows below r are still empty). If a row has no safe column, backtrack.
Algorithm
- Initialise a
char[n][n]board of'.'. solve(r): ifr == n, convert the board to strings and record it.- For each column
c: ifisSafe(r, c), set'Q',solve(r + 1), reset to'.'. isSafechecks(i, c),(i, c - (r - i)),(i, c + (r - i))for everyi < r.
Visualization
n = 4, first solution
row 0: try c=0 -> later dead end; try c=1
. Q . .
row 1: c=0,1,2 attacked; c=3 safe
. Q . .
. . . Q
row 2: c=0 safe
Q . . .
row 3: c=2 safe -> solution [".Q..","...Q","Q...","..Q."]Code
import java.util.*;
class Solution {
public List<List<String>> solveNQueens(int n) {
char[][] board = new char[n][n];
for (char[] row : board) Arrays.fill(row, '.');
List<List<String>> result = new ArrayList<>();
solve(board, 0, result);
return result;
}
private void solve(char[][] board, int r, List<List<String>> result) {
int n = board.length;
if (r == n) {
List<String> snapshot = new ArrayList<>();
for (char[] row : board) snapshot.add(new String(row));
result.add(snapshot);
return;
}
for (int c = 0; c < n; c++) {
if (!isSafe(board, r, c)) continue;
board[r][c] = 'Q';
solve(board, r + 1, result);
board[r][c] = '.'; // backtrack
}
}
// only rows above r can contain queens
private boolean isSafe(char[][] board, int r, int c) {
int n = board.length;
for (int i = r - 1, d = 1; i >= 0; i--, d++) {
if (board[i][c] == 'Q') return false; // same column
if (c - d >= 0 && board[i][c - d] == 'Q') return false; // up-left diagonal
if (c + d < n && board[i][c + d] == 'Q') return false; // up-right diagonal
}
return true;
}
}Complexity
- Time: O(n! · n) — row
rhas at mostn - rsafe columns, and each check costs O(n). - Space: O(n²) — the board, plus O(n) recursion.
Backtracking with Column / Diagonal Sets
TimeO(n!)SpaceO(n²)Intuition
Instead of scanning, remember which lines are occupied. All squares on a main diagonal share r - c (range -(n-1)..n-1, shift by n - 1 to index an array), and all squares on an anti-diagonal share r + c (range 0..2n-2). Three boolean arrays make the safety check O(1).
Algorithm
cols[n],diag[2n-1](indexr - c + n - 1),anti[2n-1](indexr + c).- In row
r, for eachcwhere none of the three is set:- Mark all three, place
'Q', recurse tor + 1, then unmark and remove.
- Mark all three, place
Visualization
flowchart TD
R["row 0"] -->|"c=0"| A["row 1: safe c = 2 or 3"]
R -->|"c=1"| B["row 1: only c=3 safe"]
A -->|"c=2"| A1["row 2: no safe column ✗"]
A -->|"c=3"| A2["row 2: c=1"]
A2 --> A3["row 3: no safe column ✗"]
B -->|"c=3"| B1["row 2: c=0"]
B1 --> B2["row 3: c=2 ✓ solution 1"]
R -->|"c=2"| C["mirror of c=1"]
C --> C1["✓ solution 2"]
R -->|"c=3"| D["mirror of c=0 ✗"]Code
import java.util.*;
class Solution {
private int n;
private boolean[] cols, diag, anti;
private char[][] board;
private final List<List<String>> result = new ArrayList<>();
public List<List<String>> solveNQueens(int n) {
this.n = n;
cols = new boolean[n];
diag = new boolean[2 * n - 1]; // r - c + n - 1
anti = new boolean[2 * n - 1]; // r + c
board = new char[n][n];
for (char[] row : board) Arrays.fill(row, '.');
place(0);
return result;
}
private void place(int r) {
if (r == n) {
List<String> snapshot = new ArrayList<>();
for (char[] row : board) snapshot.add(new String(row));
result.add(snapshot);
return;
}
for (int c = 0; c < n; c++) {
int d = r - c + n - 1, a = r + c;
if (cols[c] || diag[d] || anti[a]) continue; // attacked
cols[c] = diag[d] = anti[a] = true;
board[r][c] = 'Q';
place(r + 1);
board[r][c] = '.';
cols[c] = diag[d] = anti[a] = false; // backtrack
}
}
}Complexity
- Time: O(n!) — each safety check is O(1); building a solution costs O(n²) per solution found.
- Space: O(n²) — the board; the three arrays are O(n).
Bitmask BacktrackingOptimal
TimeO(n!)SpaceO(n)Intuition
Store the occupied columns and diagonals as bits of three integers, relative to the current row:
cols— bitcset if columncis taken.diag— squares attacked diagonally in this row; moving to the next row shifts it left by one.anti— the other diagonal; shifts right by one.
Then free = ~(cols | diag | anti) & ((1 << n) - 1) holds every safe column at once, and free & -free extracts the lowest one. We never even look at attacked columns.
Algorithm
solve(r, cols, diag, anti): ifr == n, record the board.free = ~(cols | diag | anti) & full.- While
free != 0:bit = free & -free;c = Integer.numberOfTrailingZeros(bit).- Record
queens[r] = c;solve(r + 1, cols | bit, (diag | bit) << 1, (anti | bit) >> 1). free &= free - 1(drop that bit).
Visualization
n = 4, full = 1111 (bit c = column c)
row 0: cols=0000 diag=0000 anti=0000 free=1111 -> pick bit 0010 (c=1)
row 1: cols=0010 diag=0100 anti=0001 free=1000 -> pick c=3
row 2: cols=1010 diag=(0100|1000)<<1 = 1000 (bits above n masked off)
anti=(0001|1000)>>1 = 0100 free=~(1110)&1111 = 0001 -> c=0
row 3: cols=1011 diag=0010 anti=0010 free=0100 -> c=2 -> solutionCode
import java.util.*;
class Solution {
private int n, full;
private int[] queens; // queens[r] = column of the queen in row r
private final List<List<String>> result = new ArrayList<>();
public List<List<String>> solveNQueens(int n) {
this.n = n;
this.full = (1 << n) - 1;
this.queens = new int[n];
solve(0, 0, 0, 0);
return result;
}
private void solve(int r, int cols, int diag, int anti) {
if (r == n) {
result.add(build());
return;
}
int free = ~(cols | diag | anti) & full; // every safe column in this row
while (free != 0) {
int bit = free & -free; // lowest safe column
free &= free - 1; // remove it from the candidates
queens[r] = Integer.numberOfTrailingZeros(bit);
solve(r + 1, cols | bit, ((diag | bit) << 1) & full, (anti | bit) >> 1);
}
}
private List<String> build() {
List<String> board = new ArrayList<>();
for (int r = 0; r < n; r++) {
char[] row = new char[n];
Arrays.fill(row, '.');
row[queens[r]] = 'Q';
board.add(new String(row));
}
return board;
}
}Complexity
- Time: O(n!) — only safe columns are ever visited and each step is a few bit operations; O(n²) per recorded solution.
- Space: O(n) —
queensplus recursion depth (output excluded).
Complexity Comparison
| Approach | Time | Space |
|---|---|---|
| Board Scan | O(n! · n) | O(n²) |
| Column / Diagonal Sets | O(n!) | O(n²) |
| Bitmask | O(n!), smallest constant | O(n) |
Common Pitfalls
- Using
r - cdirectly as an array index — it is negative forc > r; offset byn - 1. - Mixing up which diagonal is
r - cand which isr + cin the bitmask shifts (<<vs>>) — the result is still a valid search only if both are consistent. - Forgetting to mask with
full—~sets all 32 bits, so columns>= nwould look free. - Forgetting to clear the three markers when backtracking.