Recursion and Backtracking Problems — Complete Interview Guide
In this tutorial, you'll learn about Recursion and Backtracking Problems. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
Recursion solves problems by breaking them into smaller instances of the same problem. Backtracking extends Recursion by exploring all candidates and abandoning partial solutions that cannot be completed.
Learning Path
flowchart LR A["Sorting & Searching"] --> B["Recursion & Backtracking
You are here"] B --> C["Heap, Stack & Queue"] C --> D["System Design Prep"] style B fill:#f90,color:#fff,stroke-width:2px
Subsets (Power Set)
Generate all subsets of a set. This is the simplest Backtracking problem and the foundation for all others.
def subsets(nums):
result = []
def backtrack(start, path):
result.append(path[:])
for i in range(start, len(nums)):
path.append(nums[i])
backtrack(i + 1, path)
path.pop()
backtrack(0, [])
return result
print(subsets([1, 2, 3]))
[[], [1], [1, 2], [1, 2, 3], [1, 3], [2], [2, 3], [3]]
import java.util.*;
public class Subsets {
public static List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
backtrack(nums, 0, new ArrayList<>(), result);
return result;
}
private static void backtrack(int[] nums, int start, List<Integer> path, List<List<Integer>> result) {
result.add(new ArrayList<>(path));
for (int i = start; i < nums.length; i++) {
path.add(nums[i]);
backtrack(nums, i + 1, path, result);
path.remove(path.size() - 1);
}
}
public static void main(String[] args) {
System.out.println(subsets(new int[]{1, 2, 3}));
}
}
[[], [1], [1, 2], [1, 2, 3], [1, 3], [2], [2, 3], [3]]
#include <vector>
#include <iostream>
using namespace std;
void backtrack(vector<int>& nums, int start, vector<int>& path, vector<vector<int>>& result) {
result.push_back(path);
for (int i = start; i < nums.size(); i++) {
path.push_back(nums[i]);
backtrack(nums, i + 1, path, result);
path.pop_back();
}
}
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> result;
vector<int> path;
backtrack(nums, 0, path, result);
return result;
}
int main() {
vector<int> nums = {1, 2, 3};
auto res = subsets(nums);
for (auto& subset : res) {
cout << "[";
for (int v : subset) cout << v << " ";
cout << "] ";
}
return 0;
}
[] [1] [1 2] [1 2 3] [1 3] [2] [2 3] [3]
N-Queens
Place N queens on an NxN board so that no two queens attack each other. This is the classic Backtracking problem with constraint checking.
def solve_n_queens(n):
col = set()
pos_diag = set() # r + c
neg_diag = set() # r - c
board = [["."] * n for _ in range(n)]
result = []
def backtrack(r):
if r == n:
result.append(["".join(row) for row in board])
return
for c in range(n):
if c in col or (r + c) in pos_diag or (r - c) in neg_diag:
continue
col.add(c)
pos_diag.add(r + c)
neg_diag.add(r - c)
board[r][c] = "Q"
backtrack(r + 1)
col.remove(c)
pos_diag.remove(r + c)
neg_diag.remove(r - c)
board[r][c] = "."
backtrack(0)
return result
solutions = solve_n_queens(4)
for sol in solutions:
print("\n".join(sol))
print()
.Q..
...Q
Q...
..Q.
..Q.
Q...
...Q
.Q..
Permutations
Generate all permutations of an array. Unlike subsets, order matters and all elements must be used.
def permute(nums):
result = []
def backtrack(path, remaining):
if not remaining:
result.append(path[:])
return
for i, num in enumerate(remaining):
path.append(num)
backtrack(path, remaining[:i] + remaining[i + 1:])
path.pop()
backtrack([], nums)
return result
print(permute([1, 2, 3]))
[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
Sudoku Solver
Fill a 9x9 Sudoku board by Backtracking through empty cells and trying valid numbers.
def solve_sudoku(board):
def is_valid(r, c, num):
for i in range(9):
if board[r][i] == num:
return False
if board[i][c] == num:
return False
box_r, box_c = 3 * (r // 3) + i // 3, 3 * (c // 3) + i % 3
if board[box_r][box_c] == num:
return False
return True
def backtrack():
for r in range(9):
for c in range(9):
if board[r][c] == ".":
for num in "123456789":
if is_valid(r, c, num):
board[r][c] = num
if backtrack():
return True
board[r][c] = "."
return False
return True
backtrack()
board = [
["5","3",".",".","7",".",".",".","."],
["6",".",".","1","9","5",".",".","."],
[".","9","8",".",".",".",".","6","."],
["8",".",".",".","6",".",".",".","3"],
["4",".",".","8",".","3",".",".","1"],
["7",".",".",".","2",".",".",".","6"],
[".","6",".",".",".",".","2","8","."],
[".",".",".","4","1","9",".",".","5"],
[".",".",".",".","8",".",".","7","9"]
]
solve_sudoku(board)
for row in board:
print(row)
['5', '3', '4', '6', '7', '8', '9', '1', '2']
['6', '7', '2', '1', '9', '5', '3', '4', '8']
['1', '9', '8', '3', '4', '2', '5', '6', '7']
['8', '5', '9', '7', '6', '1', '4', '2', '3']
['4', '2', '6', '8', '5', '3', '7', '9', '1']
['7', '1', '3', '9', '2', '4', '8', '5', '6']
['9', '6', '1', '5', '3', '7', '2', '8', '4']
['2', '8', '7', '4', '1', '9', '6', '3', '5']
['3', '4', '5', '2', '8', '6', '1', '7', '9']
Common Mistakes
- Missing base case -- Every recursive function must have a termination condition. Forgetting it causes infinite Recursion and stack overflow.
- Not restoring state -- Backtracking requires undoing changes after Recursion returns. Always pop path entries or reset board cells.
- Deep copy vs reference -- Appending
pathinstead ofpath[:]stores a reference that mutates after Backtracking. Always copy the current state. - No pruning -- Without constraint checking, Backtracking explores all possibilities including invalid ones. Prune early with validity checks.
- Stack depth limits -- Python's default Recursion limit is 1000. For problems requiring deeper Recursion, use iterative approaches or increase the limit.
- Redundant computation -- Recalculating valid candidates on each call without Caching leads to exponential time. Precompute or use incremental validation.
- Wrong parameter passing -- Passing lists by reference in C++ without copying causes unintended mutation. Use
constreferences for read-only data.
Practice Questions
1. Generate all combinations of k numbers from 1 to n.
Modify the subset pattern to stop when path length reaches k. Use pruning: if remaining numbers + current length < k, skip.
2. Letter combinations of a phone number (Leetcode 17).
Map digits to letters recursively. Build combinations by iterating over each digit's letters and recursing for the next digit.
3. Challenge: Word Search (Leetcode 79)
Given a 2D board and a word, find if the word exists. Use DFS with Backtracking, marking visited cells temporarily to avoid reuse.
FAQ
Related Tutorials
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro. Updated 2026-06-23.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro