BackTrackingProblem 2 of 19

Printing all solutions in N-Queen Problem

Brute Force
Time: O(n!)
Space: O(n²)
Optimal
Time: O(n!)
Space: O(n)

Problem Statement

The N-Queens puzzle is the problem of placing N chess queens on an N×N chessboard so that no two queens attack each other. Given an integer N, return all distinct solutions to the N-Queens puzzle. Each solution contains a distinct board configuration where 'Q' represents a queen and '.' represents an empty space.

Example:

Input: n = 4 Output: [ [".Q..", "...Q", "Q...", "..Q."], ["..Q.", "Q...", "...Q", ".Q.."] ] Explanation: There are 2 distinct solutions for 4-Queens problem.

Approach 1: Brute Force (Check All Positions)

Intuition

Place queens row by row and for each placement, check if it's safe by examining all previously placed queens. This involves checking the column, upper-left diagonal, and upper-right diagonal.

Algorithm

  1. Start from the first row
  2. Try placing a queen in each column of the current row
  3. For each placement, check if it's safe by examining all previously placed queens
  4. If safe, move to the next row
  5. If all queens are placed, store the solution
  6. Backtrack and try the next column
java
import java.util.*;

class Solution {
    public boolean isSafe(char[][] board, int row, int col, int n) {
        // Check column above
        for (int i = 0; i < row; i++) {
            if (board[i][col] == 'Q') return false;
        }
        
        // Check upper-left diagonal
        for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
            if (board[i][j] == 'Q') return false;
        }
        
        // Check upper-right diagonal
        for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
            if (board[i][j] == 'Q') return false;
        }
        
        return true;
    }
    
    public void solve(int row, char[][] board, List<List<String>> result, int n) {
        // Base case: all queens placed
        if (row == n) {
            List<String> solution = new ArrayList<>();
            for (char[] r : board) {
                solution.add(new String(r));
            }
            result.add(solution);
            return;
        }
        
        // Try placing queen in each column
        for (int col = 0; col < n; col++) {
            if (isSafe(board, row, col, n)) {
                board[row][col] = 'Q';
                solve(row + 1, board, result, n);
                board[row][col] = '.';  // Backtrack
            }
        }
    }
    
    public List<List<String>> solveNQueens(int n) {
        List<List<String>> result = new ArrayList<>();
        char[][] board = new char[n][n];
        for (char[] row : board) Arrays.fill(row, '.');
        solve(0, board, result, n);
        return result;
    }
}

Complexity Analysis

  • Time Complexity: O(n! × n) - n! arrangements, O(n) to check each
  • Space Complexity: O(n²) - For the board

Approach 2: Optimal (Using Hash Sets for O(1) Validation)

Intuition

Instead of checking safety by iterating through the board, use hash sets to track which columns and diagonals are occupied. This reduces the safety check from O(n) to O(1).

Algorithm

  1. Use three sets: columns, left diagonals (row-col), right diagonals (row+col)
  2. Before placing a queen, check if the position conflicts with any set
  3. If safe, add to sets, place queen, and recurse
  4. Backtrack by removing from sets
java
import java.util.*;

class Solution {
    private Set<Integer> cols = new HashSet<>();
    private Set<Integer> leftDiag = new HashSet<>();   // row - col
    private Set<Integer> rightDiag = new HashSet<>();  // row + col
    
    public void solve(int row, char[][] board, List<List<String>> result, int n) {
        // Base case: all queens placed
        if (row == n) {
            List<String> solution = new ArrayList<>();
            for (char[] r : board) {
                solution.add(new String(r));
            }
            result.add(solution);
            return;
        }
        
        // Try placing queen in each column
        for (int col = 0; col < n; col++) {
            // Check if position is safe using sets
            if (cols.contains(col) || leftDiag.contains(row - col) || rightDiag.contains(row + col)) {
                continue;
            }
            
            // Place queen and update sets
            board[row][col] = 'Q';
            cols.add(col);
            leftDiag.add(row - col);
            rightDiag.add(row + col);
            
            solve(row + 1, board, result, n);
            
            // Backtrack
            board[row][col] = '.';
            cols.remove(col);
            leftDiag.remove(row - col);
            rightDiag.remove(row + col);
        }
    }
    
    public List<List<String>> solveNQueens(int n) {
        List<List<String>> result = new ArrayList<>();
        char[][] board = new char[n][n];
        for (char[] row : board) Arrays.fill(row, '.');
        solve(0, board, result, n);
        return result;
    }
}

Complexity Analysis

  • Time Complexity: O(n!) - At most n choices for first row, n-1 for second, etc.
  • Space Complexity: O(n) - For the three sets (each can have at most n elements)

Key Takeaways

  1. Diagonal Property: Left diagonal (row-col) and right diagonal (row+col) have constant values
  2. Hash Set Optimization: Reduces validation from O(n) to O(1)
  3. Row by Row: Place one queen per row, no need to check row conflicts
  4. Symmetry: Solutions come in symmetric pairs (can use to reduce search space)
  5. LeetCode #51: Classic backtracking problem - N-Queens
  6. Constraint Propagation: Using sets is a form of constraint tracking