|
7 | 7 |  |
8 | 8 |
|
9 | 9 | ## Problem Explanation |
| 10 | +#### **Problem Statement** |
| 11 | +The **N-Queens problem** is about placing `N` queens on an `N x N` chessboard such that: |
| 12 | +- No two queens threaten each other. |
| 13 | +- Queens threaten in the same row, same column, or along both diagonals. |
10 | 14 |
|
11 | | -The **N-Queens Problem** is a famous backtracking problem where the goal is to place \(n\) queens on an \(n \times n\) chessboard so that: |
12 | | -1. No two queens threaten each other. |
13 | | - - A queen threatens another queen if they are in the same **row**, **column**, or **diagonal**. |
14 | | -2. You need to find all possible valid arrangements of \(n\) queens and return them in a specific format: |
15 | | - - Each solution is represented as a list of strings where: |
16 | | - - A `Q` represents a queen. |
17 | | - - A `.` represents an empty space. |
18 | | - |
19 | | -#### **Example** |
20 | | -For \(n = 4\), there are two valid solutions: |
21 | | -```plaintext |
22 | | -Solution 1: |
23 | | -[ |
24 | | - "Q...", |
25 | | - "...Q", |
26 | | - ".Q..", |
27 | | - "..Q." |
28 | | -] |
29 | | -
|
30 | | -Solution 2: |
31 | | -[ |
32 | | - "..Q.", |
33 | | - "Q...", |
34 | | - "...Q", |
35 | | - ".Q.." |
36 | | -] |
37 | | -``` |
38 | | - |
39 | | -#### **Approach** |
40 | | -To solve this problem, we can use **backtracking**. Backtracking systematically explores all possibilities to build solutions, backtracking (undoing) when a solution violates the constraints. Here's how we can approach this problem step-by-step: |
41 | | - |
42 | | - |
43 | | - |
44 | | -**1. Represent the Chessboard** |
45 | | -- Use a **2D array** (`board`) of size \(n \times n\), where: |
46 | | - - `1` indicates a queen is placed. |
47 | | - - `0` indicates an empty spot. |
48 | | -- Initially, the board is filled with `0`. |
49 | | - |
50 | | - |
51 | | - |
52 | | -**2. Solve Column-by-Column** |
53 | | -- Start from the **first column** and attempt to place a queen in any valid row. |
54 | | -- Move to the **next column** only if the queen placement in the current column is valid. |
55 | | - |
56 | | - |
| 15 | +We need to find all valid configurations for placing these queens and return them in a specific format: |
| 16 | +- Each solution is represented by a list of strings, where each string represents a row. |
| 17 | +- For example, if `N = 4`, one solution looks like: |
| 18 | + ``` |
| 19 | + .Q.. |
| 20 | + ...Q |
| 21 | + Q... |
| 22 | + ..Q. |
| 23 | + ``` |
57 | 24 |
|
58 | | -**3. Safety Check** |
59 | | -- For every cell `(row, col)` where we attempt to place a queen, we need to ensure: |
60 | | - 1. **No queen in the same row** to the left of the current column. |
61 | | - 2. **No queen in the upper-left diagonal**. |
62 | | - 3. **No queen in the lower-left diagonal**. |
63 | 25 |
|
64 | 26 |
|
| 27 | +#### **Understanding the Approach** |
65 | 28 |
|
66 | | -**4. Backtrack if a Solution Fails** |
67 | | -- If no valid row exists for the current column, backtrack: |
68 | | - - Remove the last queen placed (reset `board[row][col]` to `0`). |
69 | | - - Try the next row in the previous column. |
| 29 | +To solve this problem, we use **backtracking**, a recursive approach that explores all possible placements of queens column by column. Here’s a breakdown: |
70 | 30 |
|
| 31 | +1. **Constraints**: |
| 32 | + - Each column must have exactly one queen. |
| 33 | + - A queen's position in the current column must not conflict with queens in the previous columns. |
71 | 34 |
|
| 35 | +2. **Tracking Validity**: |
| 36 | + - To check if placing a queen at `(row, col)` is valid: |
| 37 | + - **Row**: Check if any queen already occupies this row. |
| 38 | + - **Primary diagonal**: No queen should occupy `(row - col)`. |
| 39 | + - **Secondary diagonal**: No queen should occupy `(row + col)`. |
72 | 40 |
|
73 | | -**5. Store Valid Solutions** |
74 | | -- If queens are successfully placed in all \(n\) columns, convert the `board` to the required string format and save it. |
| 41 | + These checks are implemented efficiently using hash sets. |
75 | 42 |
|
| 43 | +3. **Recursive Process**: |
| 44 | + - Start placing queens from column `0`. |
| 45 | + - For the current column: |
| 46 | + - Try placing a queen in every row. |
| 47 | + - If valid, mark the row and diagonals as occupied. |
| 48 | + - Recursively move to the next column. |
| 49 | + - If all queens are placed (base case), add the current configuration to the solution. |
| 50 | + - Backtrack by removing the queen and marking the row and diagonals as free. |
76 | 51 |
|
| 52 | +4. **Output Format**: |
| 53 | + - Store solutions as a vector of strings. |
77 | 54 |
|
78 | | -#### **Example Walkthrough** |
79 | | -Let’s consider \(n = 4\). |
80 | | - |
81 | | -1. **Start with Column 0**: |
82 | | - - Try placing a queen at `(0, 0)` (row 0, column 0). It's valid. |
83 | | - - Move to **column 1**. |
84 | | - |
85 | | -2. **Column 1**: |
86 | | - - Try `(0, 1)` → Invalid (same row as `(0, 0)`). |
87 | | - - Try `(1, 1)` → Valid. Place the queen and move to **column 2**. |
88 | | - |
89 | | -3. **Column 2**: |
90 | | - - Try `(0, 2)` → Invalid (same row as `(0, 0)`). |
91 | | - - Try `(1, 2)` → Invalid (same row as `(1, 1)`). |
92 | | - - Try `(2, 2)` → Valid. Place the queen and move to **column 3**. |
93 | | - |
94 | | -4. **Column 3**: |
95 | | - - Try `(0, 3)` → Invalid. |
96 | | - - Try `(1, 3)` → Invalid. |
97 | | - - Try `(2, 3)` → Invalid. |
98 | | - - Try `(3, 3)` → Valid. This completes one solution! |
99 | | - |
100 | | -Backtrack and explore other possibilities until all valid solutions are found. |
101 | 55 |
|
102 | 56 | ## Problem Solution |
103 | 57 | ```cpp |
@@ -156,152 +110,125 @@ class Solution { |
156 | 110 |
|
157 | 111 | ## Problem Solution Explanation |
158 | 112 |
|
159 | | -#### **1. Helper Function: `addSolution`** |
| 113 | +#### **Helper Function: `addSolution`** |
160 | 114 | ```cpp |
161 | | -void addSolution(vector<vector<string>>& ans, vector<vector<int>>& board, int n) { |
| 115 | +void addSolution(vector<vector<string>>& ans, vector<int>& board, int n){ |
162 | 116 | vector<string> temp; |
163 | | - for (int i = 0; i < n; i++) { |
164 | | - string row = ""; |
165 | | - for (int j = 0; j < n; j++) { |
166 | | - if (board[i][j] == 1) |
167 | | - row += "Q"; |
168 | | - else |
169 | | - row += "."; |
170 | | - } |
171 | | - temp.push_back(row); |
| 117 | + for(int i = 0; i < n; i++){ |
| 118 | + string row(n, '.'); // Create a string of size 'n', initialized with '.' |
| 119 | + row[board[i]] = 'Q'; // Place a queen at the column index stored in board[i] |
| 120 | + temp.push_back(row); // Add this row to the solution |
172 | 121 | } |
173 | | - ans.push_back(temp); |
| 122 | + ans.push_back(temp); // Add this configuration to the final answers |
174 | 123 | } |
175 | 124 | ``` |
176 | | -- Converts the `board` into the required string representation and adds it to the `ans` vector. |
| 125 | +- This function converts the internal `board` representation (queen positions per column) into the required string format. |
| 126 | +- **Example**: If `board = [1, 3, 0, 2]` for `N = 4`, this means: |
| 127 | + - Column `0` → Row `1` has a queen → Row: `.Q..` |
| 128 | + - Column `1` → Row `3` has a queen → Row: `...Q` |
| 129 | + - Column `2` → Row `0` has a queen → Row: `Q...` |
| 130 | + - Column `3` → Row `2` has a queen → Row: `..Q.` |
177 | 131 |
|
178 | | -#### **2. Helper Function: `isSafe`** |
179 | | -```cpp |
180 | | -bool isSafe(int row, int col, vector<vector<int>>& board, int n) { |
181 | | - int x = row; |
182 | | - int y = col; |
183 | | - |
184 | | - // Check left side of the row |
185 | | - while (y >= 0) { |
186 | | - if (board[x][y] == 1) return false; |
187 | | - y--; |
188 | | - } |
189 | 132 |
|
190 | | - // Check upper-left diagonal |
191 | | - x = row; |
192 | | - y = col; |
193 | | - while (x >= 0 && y >= 0) { |
194 | | - if (board[x][y] == 1) return false; |
195 | | - x--; |
196 | | - y--; |
197 | | - } |
198 | 133 |
|
199 | | - // Check lower-left diagonal |
200 | | - x = row; |
201 | | - y = col; |
202 | | - while (x < n && y >= 0) { |
203 | | - if (board[x][y] == 1) return false; |
204 | | - x++; |
205 | | - y--; |
206 | | - } |
207 | | - |
208 | | - return true; |
209 | | -} |
210 | | -``` |
211 | | -- Validates whether a queen can be safely placed at `(row, col)`: |
212 | | - 1. Checks all positions in the **left row**. |
213 | | - 2. Checks all positions in the **upper-left diagonal**. |
214 | | - 3. Checks all positions in the **lower-left diagonal**. |
215 | | -
|
216 | | -#### **3. Recursive Function: `solve`** |
| 134 | +#### **Recursive Function: `solve`** |
217 | 135 | ```cpp |
218 | | -void solve(int col, vector<vector<string>>& ans, vector<vector<int>>& board, int n) { |
219 | | - if (col == n) { |
220 | | - addSolution(ans, board, n); |
| 136 | +void solve(int col, vector<vector<string>>& ans, vector<int>& board, unordered_set<int>& rows, unordered_set<int>& primaryDiag, unordered_set<int>& secondaryDiag, int n){ |
| 137 | + if(col == n){ |
| 138 | + addSolution(ans, board, n); // If all columns are processed, store the current board configuration |
221 | 139 | return; |
222 | 140 | } |
223 | | -
|
224 | | - for (int row = 0; row < n; row++) { |
225 | | - if (isSafe(row, col, board, n)) { |
226 | | - board[row][col] = 1; |
227 | | - solve(col + 1, ans, board, n); |
228 | | - board[row][col] = 0; |
| 141 | + for(int row = 0; row < n; row++){ // Try placing a queen in each row of the current column |
| 142 | + int diag1 = row - col; // Primary diagonal value |
| 143 | + int diag2 = row + col; // Secondary diagonal value |
| 144 | + |
| 145 | + // Check if placing a queen is valid |
| 146 | + if(rows.find(row) == rows.end() && primaryDiag.find(diag1) == primaryDiag.end() && secondaryDiag.find(diag2) == secondaryDiag.end()){ |
| 147 | + board[col] = row; // Place the queen in the current column |
| 148 | + rows.insert(row); // Mark this row as occupied |
| 149 | + primaryDiag.insert(diag1); // Mark the primary diagonal as occupied |
| 150 | + secondaryDiag.insert(diag2); // Mark the secondary diagonal as occupied |
| 151 | + |
| 152 | + solve(col + 1, ans, board, rows, primaryDiag, secondaryDiag, n); // Recurse to the next column |
| 153 | + |
| 154 | + // Backtrack: Remove the queen and mark the row/diagonals as free |
| 155 | + board[col] = -1; |
| 156 | + rows.erase(row); |
| 157 | + primaryDiag.erase(diag1); |
| 158 | + secondaryDiag.erase(diag2); |
229 | 159 | } |
230 | 160 | } |
231 | 161 | } |
232 | 162 | ``` |
233 | | -- If \( \text{col} = n \), all queens are placed, so call `addSolution`. |
234 | | -- For each row in the current column: |
235 | | - 1. Check if it's safe to place a queen. |
236 | | - 2. If valid, place the queen, move to the next column, and backtrack if needed. |
| 163 | +- **Base Case**: When `col == n`, all queens are placed, and the current configuration is stored using `addSolution`. |
| 164 | +- **Recursive Case**: For each column, try placing a queen in every row and check if the position is valid. |
| 165 | +- **Backtracking**: Ensures that the algorithm explores all possibilities by undoing changes made for a failed path. |
| 166 | +
|
237 | 167 |
|
238 | | -#### **4. Main Function** |
| 168 | +
|
| 169 | +#### **Main Function: `solveNQueens`** |
239 | 170 | ```cpp |
240 | 171 | vector<vector<string>> solveNQueens(int n) { |
241 | | - vector<vector<int>> board(n, vector<int>(n, 0)); |
242 | | - vector<vector<string>> ans; |
243 | | - solve(0, ans, board, n); |
244 | | - return ans; |
| 172 | + vector<int> board(n, -1); // Tracks queen positions for each column (initially no queens placed) |
| 173 | + vector<vector<string>> ans; // Stores all valid solutions |
| 174 | + unordered_set<int> rows; // Tracks rows that are already occupied |
| 175 | + unordered_set<int> primaryDiag; // Tracks primary diagonals that are occupied |
| 176 | + unordered_set<int> secondaryDiag; // Tracks secondary diagonals that are occupied |
| 177 | +
|
| 178 | + solve(0, ans, board, rows, primaryDiag, secondaryDiag, n); // Start solving from the first column |
| 179 | + return ans; // Return the list of all solutions |
245 | 180 | } |
246 | 181 | ``` |
247 | | -- Initializes the board and calls `solve` starting from column 0. |
| 182 | +- Initializes all data structures and starts the recursive backtracking process. |
248 | 183 |
|
249 | 184 |
|
250 | 185 |
|
251 | 186 | ### **Step 3: Example Walkthrough** |
252 | | -Input: \( n = 4 \) |
253 | | -
|
254 | | -1. The algorithm explores all possibilities, starting with: |
255 | | - ``` |
256 | | - Q... |
257 | | - ...Q |
258 | | - .Q.. |
259 | | - ..Q. |
260 | | - ``` |
261 | | -
|
262 | | -2. After finding a solution, it backtracks to explore: |
263 | | - ``` |
264 | | - ..Q. |
265 | | - Q... |
266 | | - ...Q |
267 | | - .Q.. |
268 | | - ``` |
269 | | -
|
270 | | -Output: |
271 | | -```cpp |
272 | | -[ |
273 | | - ["Q...", "...Q", ".Q..", "..Q."], |
274 | | - ["..Q.", "Q...", "...Q", ".Q.."] |
275 | | -] |
276 | | -``` |
277 | 187 |
|
| 188 | +#### **Example: N = 4** |
| 189 | +1. Start from column `0`: |
| 190 | + - Place queen in `(0, 0)`. Mark row `0` and diagonals `(0 - 0)` and `(0 + 0)` as occupied. |
278 | 191 |
|
| 192 | +2. Move to column `1`: |
| 193 | + - Row `0` is blocked. Try row `1` → Conflicts diagonally. |
| 194 | + - Try row `2` → Valid. Place queen in `(2, 1)`. |
279 | 195 |
|
280 | | -### **Step 4: Time and Space Complexity** |
| 196 | +3. Move to column `2`: |
| 197 | + - Row `0` is blocked. Row `1` conflicts diagonally. |
| 198 | + - Try row `3` → Valid. Place queen in `(3, 2)`. |
281 | 199 |
|
282 | | -#### **Time Complexity** |
283 | | -- **Backtracking** explores all possible queen placements: |
284 | | - - The total number of recursive calls is roughly \( O(n!) \) since we attempt to place queens column by column. |
285 | | -- Checking safety involves \( O(n) \) operations for each queen. |
| 200 | +4. Move to column `3`: |
| 201 | + - Place queen in `(1, 3)`. |
| 202 | + - **Valid Configuration Found**: |
| 203 | + ``` |
| 204 | + .Q.. |
| 205 | + ...Q |
| 206 | + Q... |
| 207 | + ..Q. |
| 208 | + ``` |
286 | 209 |
|
287 | | -Overall time complexity: \( O(n! \times n) \). |
288 | | - |
289 | | -#### **Space Complexity** |
290 | | -- The `board` requires \( O(n^2) \) space. |
291 | | -- The recursion stack can grow to \( O(n) \). |
292 | | - |
293 | | -Total space complexity: \( O(n^2) \). |
294 | 210 |
|
295 | 211 |
|
| 212 | +### **Step 4: Time and Space Complexity** |
296 | 213 |
|
297 | | -### **Step 5: Recommendations** |
298 | | -1. **Practice Backtracking**: |
299 | | - - Try simpler problems like generating all subsets or permutations to strengthen your recursion and backtracking skills. |
| 214 | +#### **Time Complexity** |
| 215 | +- There are `N!` permutations for placing queens. |
| 216 | +- Validity checks for rows and diagonals take O(1). |
| 217 | +- Total time complexity: **O(N!)** |
300 | 218 |
|
301 | | -2. **Optimize Safety Check**: |
302 | | - - Instead of rechecking the board for threats, consider using hash sets for rows and diagonals to reduce the safety check to \( O(1) \). |
| 219 | +#### **Space Complexity** |
| 220 | +- Space for: |
| 221 | + - `board` array: O(N) |
| 222 | + - `rows`, `primaryDiag`, `secondaryDiag` sets: O(N) |
| 223 | + - Recursive stack: O(N) |
| 224 | +- Total space complexity: **O(N)** |
303 | 225 |
|
304 | | -3. **Visualize Solutions**: |
305 | | - - Draw the chessboard for smaller \( n \) values to understand how the queens are placed. |
306 | 226 |
|
307 | 227 |
|
| 228 | +### **Step 5: Recommendations for Students** |
| 229 | +1. **Visualize the Problem**: Draw the chessboard and manually place queens to understand constraints. |
| 230 | +2. **Practice Similar Problems**: Try variations like "Sudoku Solver" and "Knight's Tour." |
| 231 | +3. **Understand Diagonal Indexing**: |
| 232 | + - `Primary Diagonal`: `row - col` |
| 233 | + - `Secondary Diagonal`: `row + col` |
| 234 | +4. **Optimize with Bitmasking**: For larger `N`, bitmasking can reduce space usage for row and diagonal tracking. |
0 commit comments