12. Backtracking#
Theory#
Backtracking explores a decision tree: choose → explore → unchoose. Use it for combinatorial generation (permutations, subsets, combinations) and constraint satisfaction (N-Queens, Sudoku).
Template#
def backtrack(path, options):
if complete(path):
result.append(path[:]) # copy
return
for choice in options:
if not valid(choice):
continue
path.append(choice) # choose
backtrack(path, next_options) # explore
path.pop() # unchoose
Problem types#
| Type | State | Examples |
|---|---|---|
| Subsets | include / skip each element | LC 78 |
| Combinations | start index to avoid duplicates | LC 77, 39 |
| Permutations | used[] array or swap | LC 46, 47 |
| Grid search | visited cells | LC 79 Word Search |
Pruning#
Cut branches early when partial state cannot lead to a valid solution — mention this to the interviewer (e.g. remaining count too small for target sum).
Complexity: often exponential; state clearly ("O(n!) permutations" / "O(2ⁿ) subsets"). See Recursion.
Problems at a glance#
| LC | Problem | |
|---|---|---|
| 46 | Permutations | ↗ |
| 77 | Combinations | ↗ |
| 78 | Subsets | ↗ |
| 784 | Letter Case Permutation | ↗ |
| 51 | N-Queens | ↗ |
| 37 | Sudoku Solver | ↗ |
| 79 | Word Search | ↗ |
| 416 | Partition Equal Subset Sum | ↗ |
| 39 | Combination Sum | ↗ |
| 131 | Palindrome Partitioning | ↗ |
| 40 | Combination Sum II (avoid duplicates) | ↗ |
| 47 | Permutations II (handling duplicates) | ↗ |
| 90 | Subsets II | ↗ |
| 93 | Restore IP Addresses | ↗ |
| 797 | Find All Paths from Source to Destination | ↗ |
| 22 | Generate Parentheses | ↗ |
| 140 | Word Break II | ↗ |
| 17 | Letter Combinations of a Phone Number | ↗ |
1. Basic Backtracking (Permutation, Combination, Subset Generation)#
Classic problems generating permutations, combinations, or subsets via recursion and backtracking.
Problem 1: Permutations (Leetcode:46)#
Problem Statement
Given an array nums of distinct integers, return all the possible permutations. You can return the answer in any order.
Example 1:
Input: nums = [1,2,3]
Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
Example 2:
Input: nums = [0,1]
Output: [[0,1],[1,0]]
Example 3:
Input: nums = [1]
Output: [[1]]
Constraints:
1 <= nums.length <= 6-10 <= nums[i] <= 10- All the integers of
numsare unique.
Code and Explanation
At each level, pick one unused element:
- Base case: When
pathhas allnelements, append a copy toresult. - Choose: Mark
used[i], appendnums[i]topath. - Explore: Recurse to fill the next position.
- Unchoose: Pop and unmark so other branches can use
nums[i].
Time: O(n × n!) | Space: O(n) recursion depth
Fix position
start, then swap each candidate into that slot:
- Choose: Swap
nums[start]withnums[i]to placenums[i]at positionstart. - Explore: Recurse on
start + 1. - Unchoose: Swap back to restore the array before trying the next
i.
Avoids a used[] array — the prefix [0..start) is fixed by swaps.
Time: O(n × n!) | Space: O(n) recursion depth
Problem 2: Combinations (Leetcode:77)#
Problem Statement
Given two integers n and k, return all possible combinations of k numbers chosen from the range [1, n].
You may return the answer in any order.
Example 1:
Input: n = 4, k = 2
Output: [[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]
Explanation: There are 4 choose 2 = 6 total combinations.
Note that combinations are unordered, i.e., [1,2] and [2,1] are considered to be the same combination.
Example 2:
Input: n = 1, k = 1
Output: [[1]]
Explanation: There is 1 choose 1 = 1 total combination.
Constraints:
1 <= n <= 201 <= k <= n
Code and Explanation
Build combinations in increasing order so
[1,2] and [2,1] are never both generated:
- Choose: Append
numtopath. - Explore: Recurse from
num + 1(forward-only picks). - Unchoose: Pop before trying the next candidate.
Time: O(k × C(n,k)) | Space: O(k)
Process numbers 1..n one at a time. For each existing partial combination, branch into exclude
num or include num. After all numbers, filter to length k. Same combinatorial space as recursion, but explicit queue instead of call stack.
Time: O(k × C(n,k)) | Space: O(C(n,k))
Problem 3: Subsets (Leetcode:78)#
Problem Statement
Given an integer array nums of unique elements, return all possible subsets (the power set).
The solution set must not contain duplicate subsets. Return the solution in any order.
Example 1:
Input: nums = [1,2,3]
Output: [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
Example 2:
Input: nums = [0]
Output: [[],[0]]
Constraints:
1 <= nums.length <= 10-10 <= nums[i] <= 10- All the numbers of
numsare unique.
Code and Explanation
For each index, decide include or skip (skip = try next
i without choosing current):
- Record: Every recursive call records the current
pathas a subset. - Choose: Include
nums[i], recurse fromi + 1. - Unchoose: Pop and try the next element.
Time: O(n × 2ⁿ) | Space: O(n)
For each new number, duplicate every existing subset and append
num. Each element doubles the result count — equivalent to the include/exclude tree, but bottom-up.
Time: O(n × 2ⁿ) | Space: O(2ⁿ)
Problem 4: Letter Case Permutation (Leetcode:784)#
Problem Statement
Given a string s, you can transform every letter individually to be lowercase or uppercase to create another string.
Return a list of all possible strings we could create. Return the output in any order.
Example 1:
Input: s = "a1b2"
Output: ["a1b2","a1B2","A1b2","A1B2"]
Example 2:
Input: s = "3z4"
Output: ["3z4","3Z4"]
Constraints:
1 <= s.length <= 12sconsists of lowercase English letters, uppercase English letters, and digits.
Code and Explanation
At each letter, branch into lowercase and uppercase; digits have a single path:
- Choose: Set the character case (or leave digit unchanged).
- Explore: Move to
index + 1. - Unchoose: Implicit — the next branch overwrites the case at this index.
Time: O(n × 2^letters) | Space: O(n)
Build strings left to right. Each letter doubles the result set (lower + upper branches). Digits extend every string by one character. Same decision tree, explored breadth-first.
Time: O(n × 2^letters) | Space: O(2^letters)
2. Backtracking with Constraints (N-Queens, Sudoku, Graph Coloring)#
Backtracking with added constraints to prune search space.
Problem 1: N-Queens (Leetcode:51)#
Problem Statement
The n-queens puzzle is the problem of placing n queens on an n x n chessboard such that no two queens attack each other.
Given an integer n, return all distinct solutions to the n-queens puzzle. You may return the answer in any order**.
Each solution contains a distinct board configuration of the n-queens' placement, where 'Q' and '.' both indicate a queen and an empty space, respectively.
Example 1:
Input: n = 4
Output: [[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
Explanation: There exist two distinct solutions to the 4-queens puzzle as shown above
Example 2:
Input: n = 1
Output: [["Q"]]
Constraints:
1 <= n <= 9
Code and Explanation
Place one queen per row. Track occupied columns and both diagonal directions:
- Prune: Skip columns that would be attacked.
- Choose: Mark constraints, place
Q. - Explore: Recurse to next row.
- Unchoose: Clear cell and release constraints.
Time: O(n!) | Space: O(n)
Same row-by-row logic, but columns/diagonals are bitmasks — O(1) conflict checks.
diag1 shifts right each row; diag2 shifts left and wraps.
Time: O(n!) | Space: O(n)
Problem 2: Sudoku Solver (Leetcode:37)#
Problem Statement
Write a program to solve a Sudoku puzzle by filling the empty cells.
A sudoku solution must satisfy all of the following rules:
- Each of the digits
1-9must occur exactly once in each row. - Each of the digits
1-9must occur exactly once in each column. - Each of the digits
1-9must occur exactly once in each of the 93x3sub-boxes of the grid.
The '.' character indicates empty cells.
Example 1:
Input: 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"]]
Output: [["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"]]
Explanation: The input board is shown above and the only valid solution is shown below:
Constraints:
board.length == 9board[i].length == 9board[i][j]is a digit or'.'.- It is guaranteed that the input board has only one solution.
Code and Explanation
Fill empty cells in fixed order. Precompute row/col/box used digits for O(1) validity:
- Choose: Place a valid digit, update constraint sets.
- Explore: Recurse to next empty cell.
- Unchoose: Reset cell and sets if no digit leads to a solution.
Time: O(9^m) where m = empty cells | Space: O(81)
Each row/col/box stores a 9-bit mask of used digits. Available digits = bits not in
used. Faster constant-time checks than set membership.
Time: O(9^m) | Space: O(81)
Problem 3: Graph Coloring Problem (GeeksforGeeks)#
Problem Statement
Given an undirected graph with V vertices (0-indexed) and M colors, assign a color to every vertex so that no two adjacent vertices share the same color. Return true if such a coloring exists, else false.
Example 1:
Input: V = 4, edges = [[0,1],[1,2],[2,3],[3,0],[0,2]], M = 3
Output: true
Explanation: Colors [0,1,0,1] (or similar) work — no edge connects same-colored vertices.
Example 2:
Input: V = 3, edges = [[0,1],[1,2],[0,2]], M = 2
Output: false
Explanation: A triangle needs 3 colors with only 2 available.
Constraints:
1 <= V <= 201 <= M <= V
Code and Explanation
Color vertices 0..V-1 in order:
- Prune: Skip colors that conflict with already-colored neighbors.
- Choose: Assign
colortovertex. - Explore: Recurse to next vertex.
- Unchoose: Reset to
-1before trying next color.
Time: O(M^V) | Space: O(V + E)
Build a bitmask of neighbor colors already used — O(1) conflict check per color try. Same choose → explore → unchoose flow, tighter inner loop.
Time: O(M^V) | Space: O(V)
Problem 4: Word Search (Leetcode:79)#
Problem Statement
Given an m x n grid of characters board and a string word, return true if word exists in the grid.
The word can be constructed from letters of sequentially adjacent cells, where adjacent cells are horizontally or vertically neighboring. The same letter cell may not be used more than once.
Example 1:
Input: board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
Output: true
Example 2:
Input: board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "SEE"
Output: true
Example 3:
Input: board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCB"
Output: false
Constraints:
m == board.lengthn = board[i].length1 <= m, n <= 61 <= word.length <= 15boardandwordconsists of only lowercase and uppercase English letters.
Follow up: Could you use search pruning to make your solution faster with a larger board?
Code and Explanation
Try every cell as start. Match
word[index], mark visited, explore 4 neighbors, restore:
- Choose: Mark cell
#. - Explore: DFS next character.
- Unchoose: Restore original letter.
Time: O(m × n × 4^L) | Space: O(L)
Same backtracking tree with explicit
visited matrix instead of mutating the board. Clearer state restoration at the cost of O(m × n) extra space.
Time: O(m × n × 4^L) | Space: O(m × n + L)
3. Backtracking with Combinatorial Optimization (Subset Sum, Partition, Palindrome Partitioning)#
Problems where backtracking is used to explore subsets or partitions optimizing certain conditions.
Problem 1: Partition Equal Subset Sum (Leetcode:416)#
Problem Statement
Given an integer array nums, return true if you can partition the array into two subsets such that the sum of the elements in both subsets is equal or false otherwise.
Example 1:
Input: nums = [1,5,11,5]
Output: true
Explanation: The array can be partitioned as [1, 5, 5] and [11].
Example 2:
Input: nums = [1,2,3,5]
Output: false
Explanation: The array cannot be partitioned into equal sum subsets.
Constraints:
1 <= nums.length <= 2001 <= nums[i] <= 100
Code and Explanation
Find a subset summing to
total / 2:
- Prune: Stop if
curr_sum > target. - Choose: Include
nums[i], recurse fromi + 1. - Unchoose: Implicit — next loop iteration skips
nums[i].
Time: O(2ⁿ) | Space: O(n)
Same include/skip tree, but cache
(index, curr_sum) to avoid recomputing identical subproblems. Still backtracking at its core — memo prunes repeated branches.
Time: O(n × target) | Space: O(n × target)
Problem 2: Combination Sum (Leetcode:39)#
Problem Statement
Given an array of distinct integers candidates and a target integer target, return a list of all unique combinations of candidates where the chosen numbers sum to target. You may return the combinations in any order.
The same number may be chosen from candidates an unlimited number of times. Two combinations are unique if the frequency of at least one of the chosen numbers is different.
The test cases are generated such that the number of unique combinations that sum up to target is less than 150 combinations for the given input.
Example 1:
Input: candidates = [2,3,6,7], target = 7
Output: [[2,2,3],[7]]
Explanation:
2 and 3 are candidates, and 2 + 2 + 3 = 7. Note that 2 can be used multiple times.
7 is a candidate, and 7 = 7.
These are the only two combinations.
Example 2:
Input: candidates = [2,3,5], target = 8
Output: [[2,2,2,2],[2,3,3],[3,5]]
Example 3:
Input: candidates = [2], target = 1
Output: []
Constraints:
1 <= candidates.length <= 302 <= candidates[i] <= 40- All elements of
candidatesare distinct.1 <= target <= 40
Code and Explanation
Sorted implicitly by always picking from
start onward — avoids duplicate orderings like [2,3] vs [3,2]:
- Choose: Add
candidates[i]topath. - Explore: Recurse from
i(same element reusable). - Unchoose: Pop before next candidate.
Time: O(2^target) | Space: O(target)
Explicit stack simulates the recursion. Each frame branches into skip/include for every candidate from
start. Same combinatorial tree, iterative control flow.
Time: O(2^target) | Space: O(2^target)
Problem 3: Palindrome Partitioning (Leetcode:131)#
Problem Statement
Given a string s, partition s such that every substring of the partition is a palindrome. Return all possible palindrome partitioning of s.
Example 1:
Input: s = "aab"
Output: [["a","a","b"],["aa","b"]]
Example 2:
Input: s = "a"
Output: [["a"]]
Constraints:
1 <= s.length <= 16scontains only lowercase English letters.
Code and Explanation
At each position, try every palindrome substring as the next partition piece:
- Prune: Skip non-palindrome substrings.
- Choose: Append substring to
path. - Explore: Recurse from
end + 1. - Unchoose: Pop before next
end.
Time: O(n × 2ⁿ) | Space: O(n)
Precompute
palindrome[i][j] in O(n²) so each cut check is O(1). Backtracking tree is identical — preprocessing prunes the inner palindrome test.
Time: O(n² + n × 2ⁿ) | Space: O(n²)
Problem 4: Subset Sum (GeeksforGeeks)#
Problem Statement
Given a set of non-negative integers nums and a target sum, determine if any subset of nums sums to sum.
Example 1:
Input: nums = [3, 34, 4, 12, 5, 2], sum = 9
Output: true
Explanation: Subset {4, 5} sums to 9.
Example 2:
Input: nums = [3, 34, 4, 12, 5, 2], sum = 30
Output: false
Constraints:
1 <= nums.length <= 2000 <= nums[i] <= 1000 <= sum <= 10⁴
Code and Explanation
For each element, branch into include or exclude:
- Choose: Add
nums[index]to running sum, recurse. - Skip: Move to next index without adding.
- Base: Return true when sum equals target.
Time: O(2ⁿ) | Space: O(n)
Cache
(index, curr_sum) to avoid revisiting the same subproblem. Still a backtracking decision tree — memoization prunes duplicate branches.
Time: O(n × target) | Space: O(n × target)
4. Backtracking with Pruning and Early Stopping#
Optimizing backtracking using pruning techniques like sorting, bounding, or memoization.
Problem 1: N-Queens with Pruning (Leetcode:51)#
Problem Statement
The n-queens puzzle is the problem of placing n queens on an n x n chessboard such that no two queens attack each other.
Given an integer n, return all distinct solutions to the n-queens puzzle. You may return the answer in any order**.
Each solution contains a distinct board configuration of the n-queens' placement, where 'Q' and '.' both indicate a queen and an empty space, respectively.
Example 1:
Input: n = 4
Output: [[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
Explanation: There exist two distinct solutions to the 4-queens puzzle as shown above
Example 2:
Input: n = 1
Output: [["Q"]]
Constraints:
1 <= n <= 9
Code and Explanation
Pruning happens before placing each queen — conflict sets reject entire columns instantly instead of exploring deep invalid branches. Choose → explore → unchoose with aggressive early cut-off.
Time: O(n!) | Space: O(n)
available bitmask lists all non-conflicting columns in O(1). Iterate only valid columns — skips entire subtrees without per-column set lookups.
Time: O(n!) | Space: O(n)
Problem 2: Word Search with Pruning (Leetcode:79)#
Problem Statement
Given an m x n grid of characters board and a string word, return true if word exists in the grid.
The word can be constructed from letters of sequentially adjacent cells, where adjacent cells are horizontally or vertically neighboring. The same letter cell may not be used more than once.
Example 1:
Input: board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
Output: true
Example 2:
Input: board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "SEE"
Output: true
Example 3:
Input: board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCB"
Output: false
Constraints:
m == board.lengthn = board[i].length1 <= m, n <= 61 <= word.length <= 15boardandwordconsists of only lowercase and uppercase English letters.
Follow up: Could you use search pruning to make your solution faster with a larger board?
Code and Explanation
Two pruning layers: only start from cells matching
word[0], and return immediately on character mismatch before recursing. Cuts dead branches early in the choose → explore → unchoose tree.
Time: O(m × n × 4^L) | Space: O(L)
A trie over
word prunes paths that don't match any prefix — useful when searching multiple words on the same board (LC 212). For a single word, behaves like Approach 1 with prefix validation.
Time: O(m × n × 4^L) | Space: O(L)
Problem 3: Combination Sum II (avoid duplicates) (Leetcode:40)#
Problem Statement
Given a collection of candidate numbers (candidates) and a target number (target), find all unique combinations in candidates where the candidate numbers sum to target.
Each number in candidates may only be used once in the combination.
Note: The solution set must not contain duplicate combinations.
Example 1:
Input: candidates = [10,1,2,7,6,1,5], target = 8
Output:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]
Example 2:
Input: candidates = [2,5,2,1,2], target = 5
Output:
[
[1,2,2],
[5]
]
Constraints:
1 <= candidates.length <= 1001 <= candidates[i] <= 501 <= target <= 30
Code and Explanation
Sort first, then skip duplicate values at the same tree level (
i > start check):
- Choose: Add
candidates[i]. - Explore: Recurse from
i + 1(each element used once). - Unchoose: Pop before next branch.
- Prune: Break when
candidates[i] > remaining.
Time: O(2ⁿ) | Space: O(n)
Explicit choose/skip branches. After unchoosing, advance
index past all equal values to avoid duplicate combinations. Same tree, different duplicate-skipping style.
Time: O(2ⁿ) | Space: O(n)
5. Backtracking on Grids / Maze Problems#
Exploring paths or all possible moves on grids, mazes, or board games.
Problem 1: Rat in a Maze (GeeksforGeeks)#
Problem Statement
Given an n × n binary matrix maze where 1 means open and 0 means blocked, find all paths for a rat from (0, 0) to (n-1, n-1). The rat can move down (D) or right (R) only.
Example 1:
Input: maze = [[1,0,0,0],[1,1,0,1],[1,1,0,0],[0,1,1,1]]
Output: ["DRDDRR", "DRDRR", "DDRR"]
Example 2:
Input: maze = [[1,0],[1,0]]
Output: []
Constraints:
2 <= n <= 5
Code and Explanation
DFS from start, try Down then Right. Mark cells visited: choose → explore → unchoose.
Time: O(2^(2n)) | Space: O(n²)
Same tree with separate
visited matrix — preserves original maze.
Time: O(2^(2n)) | Space: O(n²)
Problem 2: Knight’s Tour Problem (GeeksforGeeks)#
Problem Statement
Given an n × n chessboard, find a sequence of knight moves that visits every cell exactly once starting from (0, 0).
Example 1:
Input: n = 5
Output: a 5×5 board with move numbers 1..25
Example 2:
Input: n = 3
Output: no solution
Constraints:
1 <= n <= 8
Code and Explanation
Sort moves by fewest onward options (Warnsdorff) to prune early. Choose → explore → unchoose on each cell placement.
Time: heavily pruned | Space: O(n²)
Try all 8 knight moves at each step. Simpler but slower — good for small boards.
Time: O(8^(n²)) | Space: O(n²)
6. Backtracking with State Restoration (Sudoku, Permutation with Duplicates)#
Backtracking problems where careful state restoration is key for correctness and efficiency.
Problem 1: Permutations II (handling duplicates) (Leetcode:47)#
Problem Statement
Given a collection of numbers, nums, that might contain duplicates, return all possible unique permutations in any order.
Example 1:
Input: nums = [1,1,2]
Output:
[[1,1,2],
[1,2,1],
[2,1,1]]
Example 2:
Input: nums = [1,2,3]
Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
Constraints:
1 <= nums.length <= 8-10 <= nums[i] <= 10
Code and Explanation
Sort + skip duplicate values at same level when previous equal is unused. Choose → explore → unchoose with
used[].
Time: O(n × n!) | Space: O(n)
At each position, swap in each unique value.
seen set skips duplicate swaps at the same level.
Time: O(n × n!) | Space: O(n)
Problem 2: Subsets II (Leetcode:90)#
Problem Statement
Given an integer array nums that may contain duplicates, return all possible subsets (the power set). The solution set must not contain duplicate subsets.
Example 1:
Input: nums = [1,2,2]
Output: [[],[1],[1,2],[1,2,2],[2],[2,2]]
Example 2:
Input: nums = [0]
Output: [[],[0]]
Constraints:
1 <= nums.length <= 10-10 <= nums[i] <= 10
Code and Explanation
Sort, then skip duplicate values at the same recursion level. Choose → explore → unchoose.
Time: O(n × 2ⁿ) | Space: O(n)
Iteratively extend subsets. Skip adding duplicate
num to the same-sized subset twice in one pass.
Time: O(n × 2ⁿ) | Space: O(2ⁿ)
Problem 3: Restore IP Addresses (Leetcode:93)#
Problem Statement
A valid IP address consists of exactly four integers separated by single dots. Each integer is between 0 and 255 (inclusive) and cannot have leading zeros.
Given a string s containing only digits, return all possible valid IP addresses by inserting dots into s.
Example 1:
Input: s = "25525511135"
Output: ["255.255.11.135","255.255.111.35"]
Example 2:
Input: s = "0000"
Output: ["0.0.0.0"]
Example 3:
Input: s = "101023"
Output: ["1.0.10.23","1.0.102.3","10.1.0.23","10.10.2.3","101.0.2.3"]
Constraints:
1 <= s.length <= 20
Code and Explanation
Build 4 segments left to right. Each segment is 1–3 digits, no leading zeros, value ≤ 255. Choose → explore → unchoose on
parts.
Time: O(3⁴) = O(81) | Space: O(4)
Enumerate all dot positions (3 cuts). Validate each of the 4 segments. Same search space as backtracking, flat iteration.
Time: O(1) | Space: O(1)
7. Backtracking with Bitmasking (Subset States, TSP)#
Using bitmasking to efficiently represent subsets or states during backtracking.
Problem 1: Traveling Salesman Problem (TSP) (GeeksforGeeks)#
Problem Statement
Given n cities and a cost matrix dist where dist[i][j] is travel cost from city i to j, find the minimum cost to visit every city exactly once and return to the start.
Example 1:
Input: dist = [[0,10,15,20],[10,0,35,25],[15,35,0,30],[20,25,30,0]]
Output: 80
Explanation: Tour 0→1→3→2→0 costs 10+25+30+15 = 80.
Example 2:
Input: dist = [[0,20],[20,0]]
Output: 40
Constraints:
2 <= n <= 15
Code and Explanation
dp[mask][u] = min cost to reach u having visited cities in mask. Extend one city at a time — bitmask encodes the subset of visited cities.
Time: O(n² × 2ⁿ) | Space: O(n × 2ⁿ)
Plain backtracking over permutations of cities 1..n-1. Prune when
cost >= best. Works for small n; use bitmask DP for larger inputs.
Time: O(n!) | Space: O(n)
Problem 2: Count number of ways/subsets using bitmasking ([Various competitive programming resources])#
Problem Statement
Given an array nums of length n, count all subsets whose elements sum to target using bitmask enumeration.
Example 1:
Input: nums = [1, 2, 3], target = 3
Output: 2
Explanation: Subsets {1, 2} and {3} sum to 3.
Example 2:
Input: nums = [1, 1, 1], target = 2
Output: 3
Constraints:
1 <= n <= 200 <= nums[i] <= 100
Code and Explanation
Each bitmask represents a subset: bit
i set means include nums[i]. Iterate all 2ⁿ masks, sum selected elements, count matches.
Time: O(n × 2ⁿ) | Space: O(1)
Recursive choose/skip with
mask tracking which elements are included. Equivalent to bitmask iteration but builds masks via backtracking.
Time: O(2ⁿ) | Space: O(n)
8. Backtracking with Graph Traversal (Paths and Cycles)#
Backtracking on graphs to find all paths, cycles, or solve Hamiltonian problems.
Problem 1: Find All Paths from Source to Destination (Leetcode:797)#
Problem Statement
Given a directed acyclic graph (DAG) of n nodes labeled from 0 to n - 1, find all possible paths from node 0 to node n - 1.
Example 1:
Input: graph = [[1,2],[3],[3],[]]
Output: [[0,1,3],[0,2,3]]
Example 2:
Input: graph = [[4,3,1],[3,2,4],[3],[4],[]]
Output: [[0,4],[0,3,4],[0,1,3,4],[0,1,2,3,4],[0,1,4]]
Constraints:
2 <= n <= 15
Code and Explanation
DFS from node 0, append each neighbor, recurse, pop. DAG guarantees no cycles — no visited set needed.
Time: O(2ⁿ × n) | Space: O(n)
Stack holds partial paths. Pop, extend by each neighbor, push back. Same exploration order as DFS.
Time: O(2ⁿ × n) | Space: O(2ⁿ × n)
Problem 2: Hamiltonian Path Problem (GeeksforGeeks)#
Problem Statement
Given an undirected graph with V vertices, determine if a Hamiltonian path exists (visits every vertex exactly once).
Example 1:
Input: V = 4, edges = [[0,1],[1,2],[2,3],[0,3]]
Output: true
Explanation: Path 0→1→2→3 visits all vertices.
Example 2:
Input: V = 3, edges = [[0,1],[1,2]]
Output: true
Constraints:
1 <= V <= 10
Code and Explanation
Try each start vertex. Choose neighbor → explore → unchoose
visited. Success when count == V.
Time: O(V!) | Space: O(V)
mask tracks visited vertices as bits — compact state for small V.
Time: O(V!) | Space: O(V)
Problem 3: Generate Parentheses (Leetcode:22)#
Problem Statement
Given n pairs of parentheses, generate all combinations of well-formed parentheses.
Example 1:
Input: n = 3
Output: ["((()))","(()())","(())()","()(())","()()()"]
Example 2:
Input: n = 1
Output: ["()"]
Constraints:
1 <= n <= 8
Code and Explanation
Add
( if opens remain; add ) only if it won't exceed opens. Choose → explore → unchoose on path.
Time: O(4ⁿ / √n) Catalan | Space: O(n)
BFS over partial strings with open/close counts. Same validity rules, explored level by level.
Time: O(4ⁿ / √n) | Space: O(4ⁿ / √n)
9. Backtracking with Memoization / Top-Down DP#
Combining backtracking with caching to avoid repeated computations in overlapping subproblems.
Problem 1: Word Break II (Leetcode:140)#
Problem Statement
Given a string s and a dictionary of strings wordDict, return all sentences that can be formed by adding spaces so that each word is in the dictionary.
Example 1:
Input: s = "catsanddog", wordDict = ["cat","cats","and","sand","dog"]
Output: ["cats and dog","cat sand dog"]
Example 2:
Input: s = "pineapplepenapple", wordDict = ["apple","pen","applepen","pine","pineapple"]
Output: ["pine apple pen apple","pineapple pen apple","pine applepen apple"]
Constraints:
1 <= s.length <= 201 <= wordDict.length <= 1000
Code and Explanation
At each position, try every dictionary word as the next segment. Memoize
backtrack(start) to reuse partial sentence lists. Choose word → explore rest → combine.
Time: O(n² × 2ⁿ) worst case | Space: O(n × 2ⁿ)
Same segmentation tree without memo — recomputes shared suffixes. Use memo (Approach 1) when
s is longer.
Time: O(n × 2ⁿ) | Space: O(n)
Problem 2: Letter Combinations of a Phone Number (Leetcode:17)#
Problem Statement
Given a string containing digits from 2-9 inclusive, return all possible letter combinations that the number could represent (phone keypad mapping).
Example 1:
Input: digits = "23"
Output: ["ad","ae","af","bd","be","bf","cd","ce","cf"]
Example 2:
Input: digits = ""
Output: []
Constraints:
0 <= digits.length <= 4
Code and Explanation
For each digit, try every mapped letter: choose → explore → unchoose. Classic combinatorial backtracking.
Time: O(4ⁿ × n) | Space: O(n)
Build combinations left to right by Cartesian product. Equivalent tree, explored iteratively.
Time: O(4ⁿ × n) | Space: O(4ⁿ)



