N Queens Hashmaps
#include <bits/stdc++.h> // Includes common standard libraries like iostream, vector, map.using namespace std; // Use the standard namespace.
// Global maps to keep track of occupied rows and diagonals for O(1) safety checks.// Using 'map' for dynamic indexing, though 'vector<bool>' would be slightly faster for fixed N.map<int, bool> rowCheck; // True if a queen is in this row index.map<int, bool> upperCheck; // True if a queen is in the diagonal (row + col) index. (Top-left to Bottom-right)map<int, bool> lowerCheck; // True if a queen is in the diagonal (N + row - col) index. (Top-right to Bottom-left) // N + row - col shifts the diagonal index to be non-negative and unique.
// Helper function to add a found solution board configuration to the overall solutions.// 'board': The current N x N board with queen placements (1 for queen, 0 for empty).// 'sol': The vector of vectors, where each inner vector is a flattened solution board.void addSolution(vector<vector<int>> board, vector<vector<int>> &sol) { vector<int> temp; // Temporary 1D vector to store the flattened board. // Iterate through each row of the 2D board. for(vector<int> row_vec : board) { // Iterate through each element (cell) in the current row. for(int cell_val : row_vec) { temp.push_back(cell_val); // Add cell value (0 or 1) to the 1D vector. } } sol.push_back(temp); // Add the flattened board configuration to the list of solutions.}
// Optimized function to check if placing a queen at (row, col) is safe.// It directly queries the global boolean maps/arrays.// 'row', 'col': Coordinates where we want to place a queen.// 'n': The size of the board (needed for lowerCheck index calculation).// Time Complexity: O(1) on average (due to map lookups).bool isSafe(int row, int col, int n) { // Check if the row is already occupied OR // if the upper diagonal (row + col) is occupied OR // if the lower diagonal (N + row - col) is occupied. // If any of these are true, it's NOT safe. return !(rowCheck[row] == true || upperCheck[row + col] == true || lowerCheck[n + row - col] == true);}
// Recursive function to solve the N-Queens problem using backtracking.// 'col': The current column where we are trying to place a queen.// 'solution': Stores all valid board configurations found (passed by reference).// 'board': The current state of the board (passed by reference to allow modification).// 'n': The size of the board.void solve(int col, vector<vector<int>> &solution, vector<vector<int>> &board, int n) { // Base Case: If all queens are placed (we have successfully placed a queen in column n-1). if(col == n) { addSolution(board, solution); // Add the current board configuration to solutions. return; // Backtrack to the previous column. }
// Try placing a queen in each row of the current 'col'. for(int row = 0; row < n; row++) { // Check if placing a queen at (row, col) is safe using the O(1) check. if(isSafe(row, col, n)) { // If safe, make the move: // 1. Place the queen on the board. board[row][col] = 1;
// 2. Update the global checks (mark row and diagonals as occupied). rowCheck[row] = true; upperCheck[row + col] = true; lowerCheck[n + row - col] = true;
// Recursive Call: Try to place the next queen in the next column. solve(col + 1, solution, board, n);
// Backtracking Step: After the recursive call returns (either found solution // or hit a dead end), undo the move: // 1. Unplace the queen from the board. board[row][col] = 0; // 2. Reset the global checks (mark row and diagonals as free). rowCheck[row] = false; upperCheck[row + col] = false; lowerCheck[n + row - col] = false; } }}
// Main function to initiate the N-Queens problem solution.// 'n': The size of the N x N chessboard.// Returns a vector of vectors representing all possible N-Queens configurations.vector<vector<int>> nQueens(int n) { vector<vector<int>> solution; // Stores all valid N-Queens solutions. // Initialize an N x N board with all 0s (empty cells). vector<vector<int>> board(n, vector<int>(n, 0));
// Clear global maps at the start to ensure a fresh state for each 'nQueens' call. // (Important if 'nQueens' is called multiple times with different 'n' values) rowCheck.clear(); upperCheck.clear(); lowerCheck.clear();
// Start the backtracking process from the first column (col = 0). solve(0, solution, board, n); return solution; // Return all found solutions.}
int main() { int n; cout << "Enter the value of n : "; cin >> n;
// Get all N-Queens solutions. vector<vector<int>> solutions = nQueens(n); int i = 0; // Counter for displaying configurations.
// Print all found solutions. if (solutions.empty()) { cout << "\nNo possible configurations for n = " << n << endl; } else { for(vector<int> config : solutions) { cout << endl << "Possible Configuration [" << ++i << "] : "; for(int val : config) { cout << val << " "; // Print 0 or 1 for each cell. } } cout << endl; // Final newline for clean output. }
return 0; // Indicate successful execution.}
1. Problem Statement
- Goal: Place
N
non-attacking queens on anN x N
chessboard. A queen can attack horizontally, vertically, and diagonally. - Output: Find all distinct configurations of queens. Each configuration is represented as an
N x N
board where1
indicates a queen and0
indicates an empty cell. - Key Constraint: No two queens can share the same row, column, or diagonal.
2. Approach: Backtracking with O(1)
Safety Checks
- Core Idea: This is an optimized version of the standard backtracking solution. Instead of iterating through parts of the board to check for conflicts (which is
O(N)
per check), we use auxiliary data structures (hash maps or boolean arrays) to keep track of occupied rows and diagonals. This allowsisSafe
checks to be performed inO(1)
time. - Conflict Conditions:
- Same Row: A queen at
(r, c)
conflicts with any other queen inrow r
. - Same Column: A queen at
(r, c)
conflicts with any other queen incolumn c
. (We avoid this by placing only one queen per column). - Upper-Left to Lower-Right Diagonal: Cells
(r, c)
on this diagonal have a constant sum ofr + c
. - Upper-Right to Lower-Left Diagonal: Cells
(r, c)
on this diagonal have a constant difference ofr - c
. To handle negative differences, we can usen - 1 + r - c
(or justr - c
and adjust map/array indexing).
- Same Row: A queen at
3. Algorithm Steps
-
Global/Auxiliary Data Structures:
map<int, bool> rowCheck
: Maps row indexr
totrue
ifrow r
has a queen.map<int, bool> upperCheck
: Maps diagonal sumr + c
totrue
if that upper-left to lower-right diagonal has a queen.map<int, bool> lowerCheck
: Maps transformed diagonal differencen + r - c
(orr - c
appropriately handled) totrue
if that upper-right to lower-left diagonal has a queen. The code usesn+row-col
as the key forlowerCheck
which corresponds toupper-right to lower-left
diagonals. Self-correction: The code’slowerCheck
variable name forrow+col
andupperCheck
forn+row-col
might be confusing, but the logic for checking these two distinct diagonal types is correct.row + col
: For diagonals that run from top-left to bottom-right (constant sum).row - col
: For diagonals that run from top-right to bottom-left (constant difference). Addingn-1
torow - col
can shift indices to be non-negative:(n - 1) + row - col
. The provided code usesn + row - col
for this.
-
isSafe(row, col, n)
Function:- Checks if placing a queen at
(row, col)
is safe. - It directly queries the
rowCheck
,upperCheck
, andlowerCheck
maps/arrays. - Returns
true
if none of these maps indicate a conflict (false
iftrue
in any). - Complexity:
O(1)
(average for map, strictO(1)
for array).
- Checks if placing a queen at
-
solve(col, solution, board, n)
(Recursive Backtracking Function):- Base Case: If
col == n
, allN
queens are placed. Addboard
tosolution
andreturn
. - Recursive Step:
- Iterate through each
row
from0
ton-1
in the currentcol
. - Check Safety: Call
isSafe(row, col, n)
. (Note:isSafe
no longer needsboard
as a parameter due to global checks). - If Safe:
- Make a Move: Place queen on
board[row][col] = 1
. - Update Auxiliary Arrays: Mark
rowCheck[row]
,upperCheck[row+col]
, andlowerCheck[n+row-col]
astrue
. - Recurse: Call
solve(col + 1, solution, board, n)
. - Backtrack:
- Unplace queen on board:
board[row][col] = 0
. - Reset auxiliary arrays: Mark
rowCheck[row]
,upperCheck[row+col]
, andlowerCheck[n+row-col]
asfalse
.
- Unplace queen on board:
- Make a Move: Place queen on
- Iterate through each
- Base Case: If
-
addSolution(board, sol)
Function:- Same as before: Converts 2D
board
to a flattened 1Dvector<int>
and adds tosol
.
- Same as before: Converts 2D
-
nQueens(int n)
Function:- Initializes
solution
andboard
. - Crucially, clears the global maps (
rowCheck
,upperCheck
,lowerCheck
) before startingsolve(0, ...)
. This ensures a fresh state for each call tonQueens
. (The provided code doesn’t explicitly clear them; ifnQueens
is called multiple times, this could be an issue. For a single call frommain
, it’s fine). - Calls
solve(0, solution, board, n)
. - Returns
solution
.
- Initializes
4. Complexity Analysis
-
Let
N
be the size of the chessboard. -
Time Complexity:
O(N!)
(Theoretically, as it explores valid permutations).- The
O(1)
isSafe
check significantly speeds up the process compared to theO(N)
version. - The
addSolution
still takesO(N^2)
time per solution. IfS
is the number of solutions, this addsO(S * N^2)
to the total. - Overall complexity is dominated by the nature of the problem, which is to find all permutations, hence
O(N!)
with constant factor from checks and board updates.
- The
-
Space Complexity:
O(N^2)
board
:O(N^2)
.- Recursion stack depth:
O(N)
. solution
:O(S * N^2)
whereS
is the number of solutions.- Auxiliary maps/arrays (
rowCheck
,upperCheck
,lowerCheck
):O(N)
forrowCheck
, andO(2*N - 1)
for each diagonal check, totalingO(N)
auxiliary space.
5. Benefits of Optimization
- Significant Speedup: For larger
N
, the difference betweenO(N!)
andO(N * N!)
is immense. - Cleaner
isSafe
: The logic inisSafe
becomes much simpler and more direct.