Recursion & Backtracking
Mind Map Summary
- Topic: Recursion & Backtracking
- Core Concepts:
- Recursion: A problem-solving technique where a function calls itself to solve smaller instances of the same problem.
- Backtracking: A general algorithm for finding all (or some) solutions to some computational problems, notably constraint satisfaction problems, that incrementally builds candidates to the solutions, and abandons a candidate (“backtracks”) as soon as it determines that the candidate cannot possibly be completed to a valid solution.
- Key Considerations:
- Base Case: The condition that stops the recursion.
- Recursive Step: The part of the function that calls itself.
- Call Stack: Be mindful of the call stack and the potential for stack overflow.
Practice Exercise
Generate all possible subsets of a given set. Find all permutations of a string. Solve the N-Queens problem. Implement a Sudoku solver.
Answer
1. Subsets:
public IList<IList<int>> Subsets(int[] nums) {
var res = new List<IList<int>>();
Backtrack(res, new List<int>(), nums, 0);
return res;
}
private void Backtrack(List<IList<int>> res, List<int> tempList, int[] nums, int start) {
res.Add(new List<int>(tempList));
for (int i = start; i < nums.Length; i++) {
tempList.Add(nums[i]);
Backtrack(res, tempList, nums, i + 1);
tempList.RemoveAt(tempList.Count - 1);
}
}
2. Permutations:
public IList<IList<int>> Permute(int[] nums) {
var res = new List<IList<int>>();
Backtrack(res, new List<int>(), nums);
return res;
}
private void Backtrack(List<IList<int>> res, List<int> tempList, int[] nums) {
if (tempList.Count == nums.Length) {
res.Add(new List<int>(tempList));
} else {
for (int i = 0; i < nums.Length; i++) {
if (tempList.Contains(nums[i])) continue;
tempList.Add(nums[i]);
Backtrack(res, tempList, nums);
tempList.RemoveAt(tempList.Count - 1);
}
}
}
3. N-Queens:
public IList<IList<string>> SolveNQueens(int n) {
var res = new List<IList<string>>();
var board = new char[n, n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
board[i, j] = ".";
}
}
Backtrack(res, board, 0, n);
return res;
}
private void Backtrack(List<IList<string>> res, char[,] board, int col, int n) {
if (col == n) {
res.Add(Construct(board, n));
return;
}
for (int i = 0; i < n; i++) {
if (IsSafe(board, i, col, n)) {
board[i, col] = "Q";
Backtrack(res, board, col + 1, n);
board[i, col] = ".";
}
}
}
private bool IsSafe(char[,] board, int row, int col, int n) {
for (int i = 0; i < col; i++) {
if (board[row, i] == "Q") return false;
}
for (int i = row, j = col; i >= 0 && j >= 0; i--, j--) {
if (board[i, j] == "Q") return false;
}
for (int i = row, j = col; j >= 0 && i < n; i++, j--) {
if (board[i, j] == "Q_") return false;
}
return true;
}
private List<string> Construct(char[,] board, int n) {
var res = new List<string>();
for (int i = 0; i < n; i++) {
res.Add(new string(board, i, 0, n));
}
return res;
}
4. Sudoku Solver:
public void SolveSudoku(char[][] board) {
if (board == null || board.Length == 0) return;
Solve(board);
}
private bool Solve(char[][] board) {
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
if (board[i][j] == ".") {
for (char c = '1'; c <= '9'; c++) {
if (IsValid(board, i, j, c)) {
board[i][j] = c;
if (Solve(board)) return true;
else board[i][j] = ".";
}
}
return false;
}
}
}
return true;
}
private bool IsValid(char[][] board, int row, int col, char c) {
for (int i = 0; i < 9; i++) {
if (board[i][col] != "." && board[i][col] == c) return false;
if (board[row][i] != "." && board[row][i] == c) return false;
if (board[3 * (row / 3) + i / 3][3 * (col / 3) + i % 3] != "." && board[3 * (row / 3) + i / 3][3 * (col / 3) + i % 3] == c) return false;
}
return true;
}