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
Nnon-attacking queens on anN x Nchessboard. A queen can attack horizontally, vertically, and diagonally. - Output: Find all distinct configurations of queens. Each configuration is represented as an
N x Nboard where1indicates a queen and0indicates 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 allowsisSafechecks 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 - cand adjust map/array indexing).
- Same Row: A queen at
3. Algorithm Steps
-
Global/Auxiliary Data Structures:
map<int, bool> rowCheck: Maps row indexrtotrueifrow rhas a queen.map<int, bool> upperCheck: Maps diagonal sumr + ctotrueif that upper-left to lower-right diagonal has a queen.map<int, bool> lowerCheck: Maps transformed diagonal differencen + r - c(orr - cappropriately handled) totrueif that upper-right to lower-left diagonal has a queen. The code usesn+row-colas the key forlowerCheckwhich corresponds toupper-right to lower-leftdiagonals. Self-correction: The code’slowerCheckvariable name forrow+colandupperCheckforn+row-colmight 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-1torow - colcan shift indices to be non-negative:(n - 1) + row - col. The provided code usesn + row - colfor this.
-
isSafe(row, col, n)Function:- Checks if placing a queen at
(row, col)is safe. - It directly queries the
rowCheck,upperCheck, andlowerCheckmaps/arrays. - Returns
trueif none of these maps indicate a conflict (falseiftruein 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, allNqueens are placed. Addboardtosolutionandreturn. - Recursive Step:
- Iterate through each
rowfrom0ton-1in the currentcol. - Check Safety: Call
isSafe(row, col, n). (Note:isSafeno longer needsboardas 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
boardto a flattened 1Dvector<int>and adds tosol.
- Same as before: Converts 2D
-
nQueens(int n)Function:- Initializes
solutionandboard. - 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; ifnQueensis 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
Nbe the size of the chessboard. -
Time Complexity:
O(N!)(Theoretically, as it explores valid permutations).- The
O(1)isSafecheck significantly speeds up the process compared to theO(N)version. - The
addSolutionstill takesO(N^2)time per solution. IfSis 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)whereSis 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 inisSafebecomes much simpler and more direct.