Every Way to Place N Non-Attacking Queens on a Board

Solve this Problem
Hard30–35 min
Topics
Companies
Practice:LeetCode ↗GFG ↗
Place n chess queens on an n×n board so that no two attack each other — no shared row, column, or diagonal — and return every distinct arrangement that achieves this. Since two queens on the same row always attack each other, placing exactly one queen per row and choosing its column is enough to describe any candidate arrangement. Checking whether a candidate column is safe by re-examining every queen placed in an earlier row works, but repeats that examination from scratch at every single column tried, at every row. Maintaining directly which columns and which diagonals are currently occupied turns that repeated re-derivation into a single lookup — the search explores the exact same tree either way, but each conflict check along the way is answered immediately instead of recomputed.

Test Case 1:

Input:n = 4
Output:[["..Q.", "Q...", "...Q", ".Q.."], [".Q..", "...Q", "Q...", "..Q."]]
Explanation:The two ways to place 4 mutually non-attacking queens on a 4×4 board.

Test Case 2:

Input:n = 1
Output:[["Q"]]
Explanation:A single queen on a 1×1 board trivially doesn't attack anything.

Test Case 3:

Input:n = 2
Output:[]
Explanation:No arrangement of 2 queens on a 2×2 board avoids all of them attacking each other.

Constraints

  • 1 ≤ n ≤ 8
  • A queen attacks any square sharing its row, column, or either diagonal
  • Each board is returned as n strings of length n, using 'Q' for a queen and '.' for an empty square
  • Results are returned sorted (each board compared row by row) for a stable, checkable answer
🚀

Try the Dry Run

Don't just read the solution — watch it execute, one step at a time.

Approach & Solutions

Brute Force — Re-Scan Every Placed Queen at Each Attempt

Brute

Place one queen per row. Before trying a column at the current row, walk every queen placed at an earlier row and check directly whether it shares that column or either diagonal — an O(row) scan repeated for every column tried, at every row. This is correct (only column-per-row placements avoid same-row conflicts by construction), but re-deriving "is this column or diagonal already taken" by re-examining every earlier queen, over and over, is exactly the same fact recomputed many times over the course of the search.

TimeO(n! · n)
SpaceO(n)
1class Solution { 2 public String[][] nonAttackingQueenBoards(int n) { 3 List<String[]> result = new ArrayList<>(); 4 int[] queenCol = new int[n]; 5 backtrack(n, 0, queenCol, result); 6 result.sort((a, b) -> { 7 for (int i = 0; i < a.length; i++) { 8 int cmp = a[i].compareTo(b[i]); 9 if (cmp != 0) return cmp; 10 } 11 return 0; 12 }); 13 return result.toArray(new String[0][]); 14 } 15 16 private void backtrack(int n, int row, int[] queenCol, List<String[]> result) { 17 if (row == n) { 18 result.add(buildBoard(n, queenCol)); 19 return; 20 } 21 for (int c = 0; c < n; c++) { 22 if (isSafe(row, c, queenCol)) { 23 queenCol[row] = c; 24 backtrack(n, row + 1, queenCol, result); 25 } 26 } 27 } 28 29 private boolean isSafe(int row, int col, int[] queenCol) { 30 for (int r = 0; r < row; r++) { 31 int c = queenCol[r]; 32 if (c == col || Math.abs(c - col) == Math.abs(r - row)) { 33 return false; 34 } 35 } 36 return true; 37 } 38 39 private String[] buildBoard(int n, int[] queenCol) { 40 String[] board = new String[n]; 41 for (int r = 0; r < n; r++) { 42 StringBuilder sb = new StringBuilder(); 43 for (int c = 0; c < n; c++) sb.append(c == queenCol[r] ? 'Q' : '.'); 44 board[r] = sb.toString(); 45 } 46 return board; 47 } 48}

Optimal — Track Used Columns and Diagonals Directly

Optimal

Instead of re-examining every earlier queen to answer "is this column or diagonal free," maintain that fact directly: one set for occupied columns, and one each for the two diagonal directions (identified by row−col and row+col, which stay constant along a diagonal). Placing a queen adds one entry to each set; removing one on backtrack deletes them again. Checking whether a column or diagonal is available becomes a single O(1) lookup instead of an O(row) walk over every previously placed queen.

TimeO(n!)
SpaceO(n)
1class Solution { 2 public String[][] nonAttackingQueenBoards(int n) { 3 List<String[]> result = new ArrayList<>(); 4 Set<Integer> cols = new HashSet<>(); 5 Set<Integer> diag1 = new HashSet<>(); 6 Set<Integer> diag2 = new HashSet<>(); 7 int[] queenCol = new int[n]; 8 backtrack(n, 0, cols, diag1, diag2, queenCol, result); 9 result.sort((a, b) -> { 10 for (int i = 0; i < a.length; i++) { 11 int cmp = a[i].compareTo(b[i]); 12 if (cmp != 0) return cmp; 13 } 14 return 0; 15 }); 16 return result.toArray(new String[0][]); 17 } 18 19 private void backtrack(int n, int row, Set<Integer> cols, Set<Integer> diag1, Set<Integer> diag2, int[] queenCol, List<String[]> result) { 20 if (row == n) { 21 result.add(buildBoard(n, queenCol)); 22 return; 23 } 24 for (int c = 0; c < n; c++) { 25 if (cols.contains(c) || diag1.contains(row - c) || diag2.contains(row + c)) continue; 26 cols.add(c); 27 diag1.add(row - c); 28 diag2.add(row + c); 29 queenCol[row] = c; 30 backtrack(n, row + 1, cols, diag1, diag2, queenCol, result); 31 cols.remove(c); 32 diag1.remove(row - c); 33 diag2.remove(row + c); 34 } 35 } 36 37 private String[] buildBoard(int n, int[] queenCol) { 38 String[] board = new String[n]; 39 for (int r = 0; r < n; r++) { 40 StringBuilder sb = new StringBuilder(); 41 for (int c = 0; c < n; c++) sb.append(c == queenCol[r] ? 'Q' : '.'); 42 board[r] = sb.toString(); 43 } 44 return board; 45 } 46}

Related Problems