N Queens Problem in DSA C++ - Time & Space Complexity
We want to understand how the time needed to solve the N Queens problem grows as the board size increases.
Specifically, how does the number of steps change when we try to place queens on larger chessboards?
Analyze the time complexity of the following code snippet.
bool isSafe(int row, int col, vector<string>& board, int n) {
for (int i = 0; i < row; i++) {
if (board[i][col] == 'Q') return false;
}
for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
if (board[i][j] == 'Q') return false;
}
for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
if (board[i][j] == 'Q') return false;
}
return true;
}
void solve(int row, vector<string>& board, vector<vector<string>>& solutions, int n) {
if (row == n) {
solutions.push_back(board);
return;
}
for (int col = 0; col < n; col++) {
if (isSafe(row, col, board, n)) {
board[row][col] = 'Q';
solve(row + 1, board, solutions, n);
board[row][col] = '.';
}
}
}
This code tries to place queens row by row on an n x n chessboard, checking safety before placing each queen.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The recursive calls that try placing queens in each row and the safety checks for each position.
- How many times: For each row, it tries all columns (n times), and for each placement, it checks safety by scanning up to the current row (up to n times).
As the board size n grows, the number of ways to place queens grows very fast because we try many column positions for each row and check safety each time.
| Input Size (n) | Approx. Operations |
|---|---|
| 4 | Hundreds of checks and recursive calls |
| 8 | Thousands to tens of thousands of checks |
| 12 | Millions of checks and recursive calls |
Pattern observation: The operations grow faster than just multiplying by n each time; it grows roughly exponentially as n increases.
Time Complexity: O(n!)
This means the time needed grows very fast, roughly like the factorial of n, because we try many queen placements and backtrack often.
[X] Wrong: "The time complexity is just O(n^2) because we have two loops for rows and columns."
[OK] Correct: The code uses recursion and backtracking, which tries many combinations, not just simple loops. This causes the time to grow much faster than n squared.
Understanding the time complexity of backtracking problems like N Queens helps you explain your approach clearly and shows you know how recursive search scales with input size.
"What if we used extra arrays to track columns and diagonals for safety checks instead of scanning the board each time? How would the time complexity change?"
Practice
N Queens Problem in chess?Solution
Step 1: Understand the problem statement
The N Queens Problem requires placing queens so that none can attack another, meaning no two queens share the same row, column, or diagonal.Step 2: Analyze the options
Place N queens on an NxN board so that no two queens attack each other correctly states the goal. Options B, C, and D contradict the problem's requirement by allowing attacks or placing queens in the same row or column.Final Answer:
Place N queens on an NxN board so that no two queens attack each other -> Option DQuick Check:
Goal = No two queens attack each other [OK]
- Thinking queens can share rows or columns
- Confusing attacking with protecting
- Ignoring diagonal attacks
Solution
Step 1: Identify correct parameter types for recursion
The function should take the current row (int) and a reference to a vector holding queen positions to track placements.Step 2: Check each option's signature
bool solveNQueens(int row, vector<int>& positions) uses int and vector<int>& correctly. void solveNQueens(vector<int> row, int positions) swaps types incorrectly. int solveNQueens(int row, int positions[]) uses array but lacks reference semantics. bool solveNQueens(int positions, vector<int> row) swaps parameter order and types incorrectly.Final Answer:
bool solveNQueens(int row, vector<int>& positions) -> Option BQuick Check:
Correct signature uses int and vector reference [OK]
- Passing vector by value causing performance issues
- Swapping parameter order
- Using wrong data types for parameters
void printBoard(const vector<int>& positions) {
for (int row = 0; row < positions.size(); ++row) {
for (int col = 0; col < positions.size(); ++col) {
if (positions[row] == col) std::cout << "Q ";
else std::cout << ". ";
}
std::cout << std::endl;
}
}
int main() {
vector<int> positions = {1, 3, 0, 2};
printBoard(positions);
return 0;
}Solution
Step 1: Understand positions vector meaning
Each index is a row; value at index is the column where queen is placed. For positions = {1,3,0,2}, queens are at (0,1), (1,3), (2,0), (3,2).Step 2: Visualize the board
Row 0: Q at col 1 -> . Q . . Row 1: Q at col 3 -> . . . Q Row 2: Q at col 0 -> Q . . . Row 3: Q at col 2 -> . . Q .Final Answer:
. Q . . . . . Q Q . . . . . Q . -> Option CQuick Check:
Positions map to board rows and columns correctly [OK]
- Mixing row and column indices
- Printing Q in wrong columns
- Ignoring zero-based indexing
bool isSafe(int row, int col, const vector<int>& positions) {
for (int i = 0; i < row; ++i) {
if (positions[i] == col || abs(positions[i] - col) == abs(i - row))
return false;
}
return true;
}Solution
Step 1: Analyze loop and conditions
The loop checks all rows before current row. It returns false if any queen shares the same column or diagonal.Step 2: Verify correctness of conditions
positions[i] == col checks column conflict. abs(positions[i] - col) == abs(i - row) checks diagonal conflict correctly.Final Answer:
No bug; the function correctly checks safety -> Option AQuick Check:
Column and diagonal checks are correct [OK]
- Loop running one step too far
- Incorrect diagonal condition
- Missing column conflict check
Solution
Step 1: Understand performance bottlenecks
Checking columns and diagonals repeatedly slows down backtracking for large N.Step 2: Evaluate optimization techniques
Using bitmasks allows O(1) checks for columns and diagonals, reducing overhead significantly compared to arrays or repeated loops.Step 3: Analyze other options
Nested loops and storing all states increase time and memory. Random placement is inefficient and unreliable.Final Answer:
Use bitmasks to track columns and diagonals instead of arrays -> Option AQuick Check:
Bitmask optimization = faster checks [OK]
- Using slow nested loops for checks
- Storing all states wastes memory
- Random placement lacks systematic pruning
Ready to prove it?
Pass the test and this pattern moves unknown
