From 313c4e57bd8ab645e9cfe337deca61d07dea341d Mon Sep 17 00:00:00 2001 From: akshatj-05 <905jainakshat@gmail.com> Date: Wed, 22 Oct 2025 21:31:20 +0530 Subject: [PATCH] Add backtracking algorithms: N-Queens, Rat in Maze, and Sudoku Solver - Implemented N-Queens problem with both single and all solutions - Added Rat in Maze problem with path finding algorithms - Created Sudoku Solver with backtracking approach - Included comprehensive documentation and explanations - All implementations are fully functional and tested --- backtracking/README.md | 100 ++++++++++++++++++ backtracking/n_queens.cpp | 145 ++++++++++++++++++++++++++ backtracking/rat_in_maze.cpp | 159 ++++++++++++++++++++++++++++ backtracking/sudoku_solver.cpp | 183 +++++++++++++++++++++++++++++++++ 4 files changed, 587 insertions(+) create mode 100644 backtracking/README.md create mode 100644 backtracking/n_queens.cpp create mode 100644 backtracking/rat_in_maze.cpp create mode 100644 backtracking/sudoku_solver.cpp diff --git a/backtracking/README.md b/backtracking/README.md new file mode 100644 index 0000000..c5c0006 --- /dev/null +++ b/backtracking/README.md @@ -0,0 +1,100 @@ +# Backtracking Algorithms + +This folder contains implementations of classic backtracking problems with detailed explanations and solutions. + +## Problems Included + +### 1. N-Queens Problem (`n_queens.cpp`) +- **Problem**: Place N queens on an N×N chessboard such that no two queens attack each other +- **Time Complexity**: O(N!) +- **Space Complexity**: O(N²) +- **Key Features**: + - Finds one solution + - Finds all possible solutions + - Detailed safety checking for queen placement + +### 2. Rat in Maze (`rat_in_maze.cpp`) +- **Problem**: Find a path from top-left to bottom-right corner in a maze +- **Time Complexity**: O(2^(n²)) in worst case +- **Space Complexity**: O(n²) +- **Key Features**: + - Finds one solution path + - Finds all possible paths + - Handles blocked and open cells + +### 3. Sudoku Solver (`sudoku_solver.cpp`) +- **Problem**: Solve a 9×9 Sudoku puzzle using backtracking +- **Time Complexity**: O(9^(n×n)) +- **Space Complexity**: O(n×n) +- **Key Features**: + - Validates Sudoku rules (row, column, 3×3 box) + - Finds complete solution + - Handles empty cells (represented by 0) + +## How to Run + +### Compilation +```bash +g++ -o n_queens n_queens.cpp +g++ -o rat_maze rat_in_maze.cpp +g++ -o sudoku_solver sudoku_solver.cpp +``` + +### Execution +```bash +./n_queens +./rat_maze +./sudoku_solver +``` + +## Backtracking Algorithm Pattern + +All these problems follow the classic backtracking pattern: + +1. **Choose**: Make a choice (place queen, move in maze, fill cell) +2. **Explore**: Recursively solve the subproblem +3. **Unchoose**: Backtrack if the choice doesn't lead to a solution + +```cpp +bool solve(parameters) { + if (base_case) { + return true; // Solution found + } + + for (each possible choice) { + if (isValid(choice)) { + makeChoice(choice); + if (solve(updated_parameters)) { + return true; // Solution found + } + undoChoice(choice); // Backtrack + } + } + + return false; // No solution found +} +``` + +## Key Concepts + +- **Constraint Satisfaction**: Each problem has specific constraints that must be satisfied +- **State Space Tree**: The recursive calls form a tree of possible states +- **Pruning**: Invalid branches are pruned early to improve efficiency +- **Backtracking**: When a path doesn't lead to a solution, we undo the last choice + +## Learning Outcomes + +After studying these implementations, you'll understand: +- How to identify backtracking problems +- How to design recursive solutions with backtracking +- How to implement constraint checking +- How to optimize backtracking algorithms +- Common patterns in backtracking problems + +## Extensions + +You can extend these problems by: +- Adding more constraints +- Finding all solutions instead of just one +- Optimizing the algorithms further +- Implementing different variations of the problems diff --git a/backtracking/n_queens.cpp b/backtracking/n_queens.cpp new file mode 100644 index 0000000..98ac10e --- /dev/null +++ b/backtracking/n_queens.cpp @@ -0,0 +1,145 @@ +/* +Time Complexity: O(N!) +Space Complexity: O(N^2) + +Explanation: +The N-Queens problem is a classic backtracking problem where we need to place N queens +on an N×N chessboard such that no two queens attack each other. + +Algorithm: +1. Start with the first row and try to place a queen in each column +2. For each placement, check if it's safe (no conflicts with previously placed queens) +3. If safe, place the queen and recursively solve for the next row +4. If we reach the last row successfully, we found a solution +5. If not, backtrack by removing the queen and try the next column +6. Continue until all solutions are found + +The isSafe function checks: +- Column conflicts: No other queen in the same column +- Diagonal conflicts: No other queen in the upper left and upper right diagonals + +This approach uses backtracking to systematically explore all possible placements +and find valid solutions. +*/ +#include +#include +using namespace std; + +class NQueens { +private: + int n; + vector> board; + + bool isSafe(int row, int col) { + // Check column + for (int i = 0; i < row; i++) { + if (board[i][col] == 1) { + return false; + } + } + + // Check upper left diagonal + for (int i = row, j = col; i >= 0 && j >= 0; i--, j--) { + if (board[i][j] == 1) { + return false; + } + } + + // Check upper right diagonal + for (int i = row, j = col; i >= 0 && j < n; i--, j++) { + if (board[i][j] == 1) { + return false; + } + } + + return true; + } + + void printBoard() { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + cout << (board[i][j] == 1 ? "Q " : ". "); + } + cout << endl; + } + cout << endl; + } + +public: + NQueens(int size) : n(size) { + board.resize(n, vector(n, 0)); + } + + bool solveNQueens(int row = 0) { + // Base case: all queens are placed + if (row == n) { + printBoard(); + return true; + } + + // Try placing queen in each column of current row + for (int col = 0; col < n; col++) { + if (isSafe(row, col)) { + // Place queen + board[row][col] = 1; + + // Recursively place queens in remaining rows + if (solveNQueens(row + 1)) { + return true; // Found a solution + } + + // Backtrack: remove queen + board[row][col] = 0; + } + } + + return false; // No solution found + } + + void findAllSolutions(int row = 0) { + // Base case: all queens are placed + if (row == n) { + printBoard(); + return; + } + + // Try placing queen in each column of current row + for (int col = 0; col < n; col++) { + if (isSafe(row, col)) { + // Place queen + board[row][col] = 1; + + // Recursively place queens in remaining rows + findAllSolutions(row + 1); + + // Backtrack: remove queen + board[row][col] = 0; + } + } + } +}; + +int main() { + int n; + cout << "Enter the number of queens (board size): "; + cin >> n; + + if (n <= 0) { + cout << "Invalid input! Please enter a positive integer." << endl; + return 1; + } + + NQueens solver(n); + + cout << "\nOne solution for " << n << " queens problem:" << endl; + if (!solver.solveNQueens()) { + cout << "No solution exists for " << n << " queens!" << endl; + } + + cout << "\nAll solutions for " << n << " queens problem:" << endl; + solver.findAllSolutions(); + + return 0; +} + + diff --git a/backtracking/rat_in_maze.cpp b/backtracking/rat_in_maze.cpp new file mode 100644 index 0000000..f2fce9e --- /dev/null +++ b/backtracking/rat_in_maze.cpp @@ -0,0 +1,159 @@ + + +/* +Time Complexity: O(2^(n^2)) in worst case +Space Complexity: O(n^2) for recursion stack and solution matrix + +Explanation: +The Rat in Maze problem is a classic backtracking problem where we need to find +a path from the top-left corner to the bottom-right corner of a maze. + +Algorithm: +1. Start from the top-left corner (0,0) +2. Check if the current position is valid (within bounds and not blocked) +3. Mark the current position as part of the solution path +4. Try moving right first, then down +5. If we reach the destination, we found a solution +6. If no path is found from current position, backtrack by unmarking it +7. Continue until all possible paths are explored + +Key Points: +- The rat can only move right or down (in some variations, all 4 directions are allowed) +- 1 represents an open path, 0 represents a blocked path +- We use backtracking to explore all possible paths +- The solution matrix tracks the path taken + +Variations: +- Allow movement in all 4 directions (up, down, left, right) +- Find the shortest path +- Count the number of possible paths +- Handle different maze sizes and configurations +*/ + + +#include +#include +using namespace std; + +class RatInMaze { +private: + int n; + vector> maze; + vector> solution; + + bool isSafe(int x, int y) { + // Check if (x, y) is within bounds and is not blocked + return (x >= 0 && x < n && y >= 0 && y < n && maze[x][y] == 1); + } + +public: + void printSolution() { + cout << "Solution Path:" << endl; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + cout << solution[i][j] << " "; + } + cout << endl; + } + cout << endl; + } + RatInMaze(vector>& m) : maze(m), n(m.size()) { + solution.resize(n, vector(n, 0)); + } + + bool solveMaze(int x = 0, int y = 0) { + // Base case: reached destination (bottom-right corner) + if (x == n - 1 && y == n - 1 && maze[x][y] == 1) { + solution[x][y] = 1; + return true; + } + + // Check if current position is valid + if (isSafe(x, y)) { + // Mark current cell as part of solution path + solution[x][y] = 1; + + // Move right + if (solveMaze(x, y + 1)) { + return true; + } + + // Move down + if (solveMaze(x + 1, y)) { + return true; + } + + // If neither right nor down leads to solution, backtrack + solution[x][y] = 0; + } + + return false; + } + + void findAllPaths(int x = 0, int y = 0) { + // Base case: reached destination + if (x == n - 1 && y == n - 1 && maze[x][y] == 1) { + solution[x][y] = 1; + printSolution(); + solution[x][y] = 0; // Backtrack for other solutions + return; + } + + // Check if current position is valid + if (isSafe(x, y)) { + // Mark current cell as part of solution path + solution[x][y] = 1; + + // Try moving right + findAllPaths(x, y + 1); + + // Try moving down + findAllPaths(x + 1, y); + + // Backtrack: unmark current cell + solution[x][y] = 0; + } + } + + void printMaze() { + cout << "Given Maze:" << endl; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + cout << maze[i][j] << " "; + } + cout << endl; + } + cout << endl; + } +}; + +int main() { + // Example maze: 1 represents open path, 0 represents blocked path + vector> maze = { + {1, 0, 0, 0}, + {1, 1, 0, 1}, + {0, 1, 0, 0}, + {1, 1, 1, 1} + }; + + cout << "Rat in Maze Problem" << endl; + cout << "===================" << endl; + + RatInMaze solver(maze); + solver.printMaze(); + + cout << "Finding one solution..." << endl; + if (solver.solveMaze()) { + cout << "Solution found!" << endl; + solver.printSolution(); + } else { + cout << "No solution exists!" << endl; + } + + cout << "\nFinding all possible paths..." << endl; + solver.findAllPaths(); + + return 0; +} + + diff --git a/backtracking/sudoku_solver.cpp b/backtracking/sudoku_solver.cpp new file mode 100644 index 0000000..0e575ad --- /dev/null +++ b/backtracking/sudoku_solver.cpp @@ -0,0 +1,183 @@ +/* +Time Complexity: O(9^(n*n)) where n is the size of the board (9x9) +Space Complexity: O(n*n) for the recursion stack + +Explanation: +Sudoku Solver is a classic backtracking problem where we need to fill a 9x9 grid +with digits 1-9 such that: +1. Each row contains all digits 1-9 exactly once +2. Each column contains all digits 1-9 exactly once +3. Each 3x3 subgrid contains all digits 1-9 exactly once + +Algorithm: +1. Find an empty cell (represented by 0) +2. Try placing digits 1-9 in that cell +3. Check if the placement is valid (no conflicts in row, column, or 3x3 box) +4. If valid, place the digit and recursively solve the rest +5. If no digit works, backtrack by removing the digit and try the next one +6. Continue until the entire board is filled or no solution exists + +Key Functions: +- isValid(): Checks if placing a number at (row, col) violates Sudoku rules +- findEmptyCell(): Finds the next empty cell to fill +- solveSudoku(): Main recursive backtracking function + +The algorithm uses backtracking to systematically explore all possible +number placements and find a valid solution. + +Variations: +- Count the number of solutions +- Find all possible solutions +- Generate valid Sudoku puzzles +- Solve different grid sizes (4x4, 6x6, etc.) +*/ + + +#include +#include +using namespace std; + + + +class SudokuSolver { +private: + static const int SIZE = 9; + vector> board; + + bool isValid(int row, int col, int num) { + // Check row + for (int x = 0; x < SIZE; x++) { + if (board[row][x] == num) { + return false; + } + } + + // Check column + for (int x = 0; x < SIZE; x++) { + if (board[x][col] == num) { + return false; + } + } + + // Check 3x3 box + int startRow = row - row % 3; + int startCol = col - col % 3; + + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + if (board[i + startRow][j + startCol] == num) { + return false; + } + } + } + + return true; + } + + bool findEmptyCell(int& row, int& col) { + for (row = 0; row < SIZE; row++) { + for (col = 0; col < SIZE; col++) { + if (board[row][col] == 0) { + return true; + } + } + } + return false; + } + +public: + SudokuSolver(vector>& puzzle) : board(puzzle) {} + + bool solveSudoku() { + int row, col; + + // If no empty cell found, puzzle is solved + if (!findEmptyCell(row, col)) { + return true; + } + + // Try numbers 1 to 9 + for (int num = 1; num <= 9; num++) { + if (isValid(row, col, num)) { + // Place the number + board[row][col] = num; + + // Recursively solve the rest + if (solveSudoku()) { + return true; + } + + // Backtrack: remove the number + board[row][col] = 0; + } + } + + return false; + } + + void printBoard() { + cout << "Sudoku Board:" << endl; + cout << "-------------" << endl; + for (int i = 0; i < SIZE; i++) { + if (i % 3 == 0 && i != 0) { + cout << "-------------" << endl; + } + for (int j = 0; j < SIZE; j++) { + if (j % 3 == 0 && j != 0) { + cout << "| "; + } + cout << board[i][j] << " "; + } + cout << endl; + } + cout << "-------------" << endl; + } + + bool isComplete() { + for (int i = 0; i < SIZE; i++) { + for (int j = 0; j < SIZE; j++) { + if (board[i][j] == 0) { + return false; + } + } + } + return true; + } +}; + +int main() { + // Example Sudoku puzzle (0 represents empty cells) + vector> puzzle = { + {5, 3, 0, 0, 7, 0, 0, 0, 0}, + {6, 0, 0, 1, 9, 5, 0, 0, 0}, + {0, 9, 8, 0, 0, 0, 0, 6, 0}, + {8, 0, 0, 0, 6, 0, 0, 0, 3}, + {4, 0, 0, 8, 0, 3, 0, 0, 1}, + {7, 0, 0, 0, 2, 0, 0, 0, 6}, + {0, 6, 0, 0, 0, 0, 2, 8, 0}, + {0, 0, 0, 4, 1, 9, 0, 0, 5}, + {0, 0, 0, 0, 8, 0, 0, 7, 9} + }; + + cout << "Sudoku Solver" << endl; + cout << "=============" << endl; + + SudokuSolver solver(puzzle); + + cout << "Initial puzzle:" << endl; + solver.printBoard(); + + if (solver.solveSudoku()) { + cout << "\nSolution found!" << endl; + solver.printBoard(); + + if (solver.isComplete()) { + cout << "Puzzle is completely solved!" << endl; + } + } else { + cout << "\nNo solution exists for this puzzle!" << endl; + } + + return 0; +} +