
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:
- 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.
- 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.
- Move to the next row and repeat the process, placing a queen and checking for conflicts.
- If a valid position is found for a row, continue to the next row.
- If a queen is successfully placed on the last row without conflicts, this configuration is a valid solution, and it should be counted.
- 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.
- 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.
- For
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.
- For
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
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 callsexploreBoard
.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.
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.
- Uses three sets to keep track of blocked diagonals (
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.
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, andUT_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:
- Set up the board size and initialize hash sets for columns, diagonals, and anti-diagonals.
- Call
solve_n_queens
starting from the first row (row 0). - 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.
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 threeSet()
objects to track columns and diagonals (diag1
anddiag2
) 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
equalsboardSize
, 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
anddiag2
) 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.
- If placing a queen in the current column and diagonals (
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.
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:
- A class named
Solution
is defined. - Inside this class, the method
totalNQueens
takes an integern
, representing the size of the chessboard and the number of queens to be placed. - 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
, andanti_diag
sets and continues exploring other columns.
- The base case checks if a row number equals
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.
No comments yet.