N-Queens II

Updated on 02 July, 2025
N-Queens II header image

Problem Statement

The n-queens puzzle is an intriguing and well-known challenge in computer science, particularly in algorithms related to backtracking. The challenge revolves around placing n queens on an n x n chessboard in a way that no two queens threaten each other. This entails ensuring that no two queens share the same row, column, or diagonal. Given the parameter n, which determines the size of the board (and the number of queens), the objective is to find the number of possible arrangements or configurations that meet these criteria.

Examples

Example 1

Input:

n = 4

Output:

2

Explanation:

There are two distinct solutions to the 4-queens puzzle as shown.

Example 2

Input:

n = 1

Output:

1

Constraints

  • 1 <= n <= 9

Approach and Intuition

The n-queens puzzle can be tackled efficiently using backtracking, a method that builds candidate solutions incrementally and abandons a candidate ("backtracks") as soon as it determines that the candidate cannot possibly be completed to a valid solution. Here's an intuitive breakdown of the solution:

  1. Start placing a queen in the first row and move column by column to check if placing the queen there does not lead to a conflict with previously placed queens.
  2. Use three markers (arrays or hash sets) to keep track of columns, and two types of diagonals (left and right) to check if a queen placed at any given point conflicts with any previously placed queens:
    • Rely on a "columns" marker to ensure no two queens share the same column.
    • Utilize a "major diagonal" marker to ensure no two queens are on the same major diagonal.
    • Use a "minor diagonal" marker to check for conflicts on the minor diagonal.
  3. Move to the next row and repeat the process, placing a queen and checking for conflicts.
  4. If a valid position is found for a row, continue to the next row.
  5. If a queen is successfully placed on the last row without conflicts, this configuration is a valid solution, and it should be counted.
  6. If a conflict arises and no position in the current row can accommodate a queen, backtrack to the previous row, move the queen to the next possible position, and try again.
  7. Continue this process recursively until all possible configurations are explored.

To explain with the examples given:

  • Example 1:

    • For n = 4, there are two configurations that allow for placing all queens without them attacking each other. The board accommodating the queens in these two distinct patterns qualifies for counting as two solutions.
  • Example 2:

    • For n = 1, only one queen on a 1x1 board trivially meets the criteria, resulting in one distinct solution since there are no possibilities for attacks.

This recursive approach ensures that all potential board configurations are explored, and by using efficient checks for placing each queen, the solution space is tremendously reduced, leading to a feasible computational effort even at the higher end of the constraint limits.

Solutions

  • C++
  • Java
  • C
  • JavaScript
  • Python
cpp
class Solution {
private:
    int boardSize;
    
public:
    int computeNQueens(int n) {
        unordered_set<int> diag, antiDiag, occupiedCols;
        boardSize = n;
        return exploreBoard(0, diag, antiDiag, occupiedCols);
    }
    
private:
    int exploreBoard(int row, unordered_set<int>& diag,
                  unordered_set<int>& antiDiag, unordered_set<int>& occupiedCols) {
        if (row == boardSize) {
            return 1;
        }
        int count = 0;
        for (int col = 0; col < boardSize; col++) {
            int diagonal = row - col;
            int antiDiagonal = row + col;
            if (occupiedCols.count(col) || diag.count(diagonal) || antiDiag.count(antiDiagonal)) {
                continue;
            }
            occupiedCols.insert(col);
            diag.insert(diagonal);
            antiDiag.insert(antiDiagonal);
            count += exploreBoard(row + 1, diag, antiDiag, occupiedCols);
            occupiedCols.erase(col);
            diag.erase(diagonal);
            antiDiag.erase(antiDiagonal);
        }
        return count;
    }
};

The provided C++ code solves the N-Queens II problem, which seeks to determine the number of possible arrangements for n queens on an n x n chessboard such that no two queens threaten each other. The solution utilizes a backtracking algorithm implemented through the computeNQueens and exploreBoard functions within the Solution class.

  • computeNQueens initializes sets to track columns and diagonals occupied by queens (diag, antiDiag, occupiedCols) and then calls exploreBoard.
  • exploreBoard attempts to place a queen in each column of the current row and recursively explores subsequent rows:
    • It checks if a column, diagonal, or anti-diagonal is occupied. If so, it skips to the next possibility.
    • If placement does not lead to a conflict, it marks the column, diagonal, and anti-diagonal as occupied and moves to the next row.
    • After exploring all options for the current row setup, it backtracks by unmarking the occupied paths and continues exploring other possibilities.
  • The function returns the count of valid arrangements.

This approach ensures you efficiently explore all potential board configurations using the principle of exclusion, leveraging the unorder_set for quick insertions, deletions, and lookups. The solution thus operates within a feasible time complexity for typical n-queen problem sizes, providing an efficient way to count solutions without maintaining full board states.

java
class NQueensSolver {
    private int boardSize;
    
    public int calculateTotalNQueens(int n) {
        boardSize = n;
        return searchPositions(0, new HashSet<>(), new HashSet<>(), new HashSet<>());
    }
    
    private int searchPositions(
        int row,
        Set<Integer> diag,
        Set<Integer> antiDiag,
        Set<Integer> columns
    ) {
        if (row == boardSize) {
            return 1;
        }
    
        int count = 0;
        for (int col = 0; col < boardSize; col++) {
            int currDiag = row - col;
            int currAntiDiag = row + col;
            if (columns.contains(col) || diag.contains(currDiag) || antiDiag.contains(currAntiDiag)) {
                continue;
            }
    
            columns.add(col);
            diag.add(currDiag);
            antiDiag.add(currAntiDiag);
    
            count += searchPositions(row + 1, diag, antiDiag, columns);
    
            columns.remove(col);
            diag.remove(currDiag);
            antiDiag.remove(currAntiDiag);
        }
    
        return count;
    }
}

The Java solution provided solves the "N-Queens II" problem, focusing on finding the total number of distinct ways to place n queens on an n x n chessboard so that no two queens threaten each other.

The solution employs a backtracking algorithm, encapsulated within the NQueensSolver class. The main logic resides in the method calculateTotalNQueens, which initializes the process and calls searchPositions method to explore all possible board configurations.

  • The calculateTotalNQueens method:

    • Takes the size of the board (n) as input.
    • Returns the total count of valid queen placements using the helper function searchPositions.
  • searchPositions method:

    • Uses three sets to keep track of blocked diagonals (diag) and anti-diagonals (antiDiag), as well as columns (columns).
    • Recursively attempts to place a queen in each row and valid column.
    • Continues to the next rows using recursion until all rows are considered, which either results in a valid placement (increments the count) or a backtracking step (removal of the queen).
    • Manages queen placement to ensure no two queens share the same row, column, or diagonal, adhering strictly to the constraints of the problem.

This approach ensures an efficient scan through all potential board setups without redundant checks, leveraging set operations to effectively manage state during recursion and backtracking. This method offers a clear and systematic strategy towards solving the N-Queens II problem by focusing on each row and potential column, ensuring all placed queens maintain the non-threatening positional requirement.

c
struct pseudo_set {
    int identifier;
    UT_hash_handle hh;
};
    
int pseudo_set_contains(struct pseudo_set *head, int identifier) {
    struct pseudo_set *entry;
    HASH_FIND_INT(head, &identifier, entry);
    return entry != NULL;
}
    
void pseudo_set_insert(struct pseudo_set **head, int identifier) {
    struct pseudo_set *entry = (struct pseudo_set *)malloc(sizeof(struct pseudo_set));
    entry->identifier = identifier;
    HASH_ADD_INT(*head, identifier, entry);
}
    
void pseudo_set_remove(struct pseudo_set **head, int identifier) {
    struct pseudo_set *entry;
    HASH_FIND_INT(*head, &identifier, entry);
    if (entry) {
        HASH_DEL(*head, entry);
    }
}
    
int board_size;
    
int solve_n_queens(int row, struct pseudo_set *diagonals,
                   struct pseudo_set *antiDiagonals, struct pseudo_set *cols) {
    if (row == board_size) {
        return 1;
    }
    int count = 0;
    for (int col = 0; col < board_size; col++) {
        int diag = row - col;
        int antiDiag = row + col;
        if (pseudo_set_contains(cols, col) ||
            pseudo_set_contains(diagonals, diag) ||
            pseudo_set_contains(antiDiagonals, antiDiag)) {
            continue;
        }
        pseudo_set_insert(&cols, col);
        pseudo_set_insert(&diagonals, diag);
        pseudo_set_insert(&antiDiagonals, antiDiag);
        count += solve_n_queens(row + 1, diagonals, antiDiagonals, cols);
        pseudo_set_remove(&cols, col);
        pseudo_set_remove(&diagonals, diag);
        pseudo_set_remove(&antiDiagonals, antiDiag);
    }
    return count;
}
    
int totalNQueens(int n) {
    board_size = n;
    struct pseudo_set *cols = NULL, *diagonals = NULL, *antiDiagonals = NULL;
    return solve_n_queens(0, diagonals, antiDiagonals, cols);
}

The "N-Queens II" solution in C language uses a backtracking approach to count the number of possible solutions for placing N queens on an N×N chessboard, where no two queens threaten each other. The major components of this implementation include a pseudo set data structure and functions to handle set operations, combined with a recursive function to explore all feasible board configurations.

  • The pseudo_set structure represents a set to track columns, diagonals, and anti-diagonals where queens are placed. identifier is used to store unique values representing specific board positions, and UT_hash_handle hh is a utility from the uthash library to manage hashing.

  • pseudo_set_contains checks if a particular identifier exists in the hash table, indicating if a queen's position conflicts with previously placed queens.

  • pseudo_set_insert adds a new identifier to the set. This function helps to keep track of board positions currently threatened by queens.

  • pseudo_set_remove deletes an identifier from the set, enabling the backtracking process by removing constraints when moving back in the decision tree.

  • solve_n_queens is a recursive function that tries to place a queen in every column of a given row. It checks for collisions using the sets for columns, diagonals, and anti-diagonals:

    • If a queen placement does not cause a conflict, it recursively attempts to place queens in the next row.
    • Upon reaching the last row (row == board_size), it increments the count of valid configurations.
  • totalNQueens is the initial function called to start the process. It initializes the board size and the hash tables, then calls solve_n_queens.

Steps for the function totalNQueens include:

  1. Set up the board size and initialize hash sets for columns, diagonals, and anti-diagonals.
  2. Call solve_n_queens starting from the first row (row 0).
  3. Return the count of valid configurations from the recursive function calls.

This solution effectively leverages hash tables for fast lookup, insertion, and deletion, making the function more efficient for larger board sizes. The algorithm systematically explores each possibility via backtracking, ensuring all potential board configurations are considered.

js
var queenSolutions = function (boardSize) {
    function search(row, diag1, diag2, columns) {
        if (row == boardSize) {
            return 1;
        }
        let count = 0;
        for (let col = 0; col < boardSize; col++) {
            let diagonal = row - col;
            let antiDiagonal = row + col;
            if (
                columns.has(col) ||
                diag1.has(diagonal) ||
                diag2.has(antiDiagonal)
            ) {
                continue;
            }
            columns.add(col);
            diag1.add(diagonal);
            diag2.add(antiDiagonal);
            count += search(row + 1, diag1, diag2, columns);
            columns.delete(col);
            diag1.delete(diagonal);
            diag2.delete(antiDiagonal);
        }
        return count;
    }
    return search(0, new Set(), new Set(), new Set());
};

The solution employs a backtracking approach to solve the N-Queens II problem using JavaScript. This problem aims to count all the possible ways to place N queens on an N x N chessboard such that no two queens threaten each other. Each queen can attack any other queen along the same row, column, or diagonal.

Understand the function queenSolutions which takes a single parameter boardSize representing the size of the chessboard (N):

  • The inner function search is recursive and is used to explore each row from 0 to N-1. It utilizes three Set() objects to track columns and diagonals (diag1 and diag2) where queens have been placed:

    • columns tracks which columns are currently occupied by queens.
    • diag1 tracks the primary diagonals (from top-left to bottom-right) occupied by queens.
    • diag2 tracks the secondary diagonals (from top-right to bottom-left) occupied by queens.
  • The recursion terminates when row equals boardSize, indicating all rows are successfully filled with queens without any conflicts. In this base case, the function returns 1 to count this unique configuration.

  • For each row, the loop iterates over each column to attempt placing a queen:

    • If placing a queen in the current column and diagonals (diag1 and diag2) is conflict-free (i.e., these sets do not contain the current column and diagonal indices), then proceed:
      • Add the current column and diagonal indices to their respective sets.
      • Recursively call search to attempt to fill the next row.
      • After recursive call, backtrack by removing the current column and diagonal indices from their respective sets, ensuring the exploration of other possibilities.
  • The count variable accumulates the number of valid arrangements for each row and ultimately yields the total count of valid configurations for the board.

Finally, the queenSolutions function initializes the search with an initial set of empty tracking sets and row set to 0. The outcome of this function gives the total number of ways to arrange N queens on an N x N board without any two queens attacking each other.

python
class Solution:
    def totalNQueens(self, n):
        def search_positions(row, diags, anti_diags, columns):
            if row == n:
                return 1
    
            number_of_solutions = 0
            for col in range(n):
                diag = row - col
                anti_diag = row + col
                if col in columns or diag in diags or anti_diag in anti_diags:
                    continue
    
                columns.add(col)
                diags.add(diag)
                anti_diags.add(anti_diag)
    
                number_of_solutions += search_positions(row + 1, diags, anti_diags, columns)
    
                columns.remove(col)
                diags.remove(diag)
                anti_diags.remove(anti_diag)
    
            return number_of_solutions
    
        return search_positions(0, set(), set(), set())

The N-Queens II problem involves finding the total number of distinct solutions to placing N queens on an N x N chessboard so that no two queens attack each other. The solution is implemented in Python using a backtracking approach to explore all feasible placements of queens.

Here's a breakdown of the solution:

  1. A class named Solution is defined.
  2. Inside this class, the method totalNQueens takes an integer n, representing the size of the chessboard and the number of queens to be placed.
  3. The main action happens in the nested function search_positions, which recursively tries to place queens on the chessboard:
    • The base case checks if a row number equals n, indicating all queens are placed successfully, returning a count of 1.
    • A loop runs through each column of the current row to attempt placing a queen.
    • Conflict checks are performed using three sets:
      • columns checks for vertical threats.
      • diags checks for diagonal threats from the top-left to bottom-right.
      • anti_diags checks for diagonal threats from the top-right to bottom-left.
    • If no conflicts are detected, the queen's column and diagonal indices get added to their respective sets and the function recursively calls itself with the next row.
    • After recursively exploring the deeper rows, it backtracks by removing the queen from the column, diag, and anti_diag sets and continues exploring other columns.

The returned value from totalNQueens gives the total number of valid configurations for the N queens on the board. This approach effectively navigates all possible board configurations using backtracking, adding successful configurations to the solution counter, and ensuring efficient conflict checks with sets.

Comments

No comments yet.