Backtracking
Expert Answer & Key Takeaways
Combination Sum
The Goal: Finding Target Sum
candidates and a target integer, find all unique combinations where the chosen numbers sum to target. You may return the combinations in any order.Key Rule: Unlimited Re-use
candidates an unlimited number of times. Two combinations are unique if the frequency of at least one of the chosen numbers is different.Decision Tree Branching
- Use the current element: Subtract its value from the target and stay at the same index (because we can re-use it).
- Skip the current element: Move to the next index in the array.
candidates that sum to target (reuse allowed). Includes visual decision tree.Examples
Level I: Basic Backtracking (Include/Exclude)
Intuition
target == 0, we found a valid combination. If target < 0 or we've used all candidates, we backtrack.Detailed Dry Run
Dry Run: target=7, candidates=[2,3]
| Step | idx | target | path | Action |
| :--- | :-- | :----- | :------ | :---------------- |
| 1 | 0 | 7 | [] | Start |
| 2 | 0 | 5 | [2] | Use 2 |
| 3 | 0 | 3 | [2,2] | Use 2 |
| 4 | 0 | 1 | [2,2,2] | Use 2 |
| 5 | 0 | -1 | [2,2,2] | FAIL (Backtrack) |
| 6 | 1 | 1 | [2,2] | Skip 2, Try 3 |
| 7 | 1 | -2 | [2,2,3] | FAIL (Backtrack) |
| 8 | 1 | 7 | [] | Skip all for id=0 |Level II: Optimized Backtracking (Loop-Based + Sorting)
Intuition
target - candidates[i] < 0, stop the loop (pruning).Detailed Dry Run
- Try 2. Recurse 5. Try 2. Recurse 3. Try 2. Recurse 1. Try 2 (FAIL - 3rd 2 is fine, 4th fails). Break.
- Backtrack to target 3. Try 3. Recurse 0. SUCCESS [2,2,3].
Level III: Iterative DP (Unbounded Knapsack Style)
Intuition
dp[i] stores all unique combinations summing to i.dp[0] with an empty combination. For each candidate, iterate from candidate to target, adding the candidate to all combinations in dp[i - candidate] and storing them in dp[i].Detailed Dry Run
- dp[0] = [[]], dp[1,2,3] = []
- Use 1: dp[1]=[[1]], dp[2]=[[1,1]], dp[3]=[[1,1,1]]
- Use 2: dp[2]=[[1,1],[2]], dp[3]=[[1,1,1],[1,2]]
Permutations
Exploration vs. Selection
[1, 2] is different from [2, 1]).Key Backtracking Strategies
- Visited Array: Maintain a boolean array to track which elements are already included in the current permutation. This is intuitive but uses extra space.
- Swap-Based: Swap the current element with every element to its right, recurse, and then swap back. This is highly efficient as it uses the input array for state.
nums of distinct integers, return all possible permutations. Includes visual state-space tree.Examples
Level I: Backtracking with Visited Array
Intuition
Detailed Dry Run
Dry Run: nums=[1,2]
| Path | Visited | Action |
| :--- | :--- | :--- |
| [] | {F, F} | Start |
| [1] | {T, F} | Choose 1 |
| [1,2] | {T, T} | Choose 2 (Result!) |
| [1] | {T, F} | Backtrack |
| [] | {F, F} | Backtrack |
| [2] | {F, T} | Choose 2 |
| [2,1] | {T, T} | Choose 1 (Result!) |Level II: Backtracking with Swapping (In-Place)
Intuition
first, swap nums[first] with every element from first to N-1. Recurse for first + 1. This 'fixes' one element and permutes the rest.Detailed Dry Run
- Swap(0,0): nums=[1,2,3]. Recurse first=1. 2. Swap(1,1): nums=[1,2,3]. Recurse first=2. SUCCESS [1,2,3]. 3. Swap(1,2): nums=[1,3,2]. Recurse first=2. SUCCESS [1,3,2].
- Swap(0,1): nums=[2,1,3]. Recurse first=1...
Level III: Iterative Lexicographical Order
Intuition
Detailed Dry Run
- Initial: [1,2,3]
- Next: [1,3,2]
- Next: [2,1,3]
- Next: [2,3,1]
- Next: [3,1,2]
- Next: [3,2,1]
Subsets
The Power Set
Inclusion-Exclusion Principle
Examples
Level I: Recursive Backtracking (Include/Exclude)
Intuition
nums[i] to our subset or not.dfs(idx, current). In one branch, add nums[idx] and recurse. In the other, don't add it and recurse.Detailed Dry Run
Dry Run: nums=[1,2]
| idx | path | Decision | Next State |
| :-- | :---: | :--- | :--- |
| 0 | [] | Include 1| DFS(1, [1])|
| 1 | [1] | Include 2| DFS(2, [1,2]) -> RES: [1,2]|
| 1 | [1] | Exclude 2| DFS(2, [1]) -> RES: [1]|
| 0 | [] | Exclude 1| DFS(1, []) |
| 1 | [] | Include 2| DFS(2, [2]) -> RES: [2]|
| 1 | [] | Exclude 2| DFS(2, []) -> RES: []|Level II: Cascading (Iterative Generation)
Intuition
[[]]. For each number in nums, duplicate all existing subsets and add the current number to the duplicates.Detailed Dry Run
- res = [[]]
- num=1: res = [[], [1]]
- num=2: res = [[], [1], [2], [1,2]]
Level III: Bit Manipulation (Bitmask)
Intuition
i = 0 to 2^N - 1. For each i, if the -th bit is 1, include nums[j] in the current subset.Detailed Dry Run
- i=0 (00): []
- i=1 (01): [1]
- i=2 (10): [2]
- i=3 (11): [1,2]
Subsets II
Handling Duplicates
The Skip Condition
nums[i] == nums[i-1] and we are at the same recursive level (not following the inclusion branch), we skip the current element to avoid repeating the same starting subset.Examples
Level I: Backtracking with Sorting
Intuition
i > start and nums[i] == nums[i-1], we skip to the next iteration.Detailed Dry Run
nums=[1,2,2], start=1
1. i=1, pick nums[1]=2 -> subset [1,2]
2. i=2, nums[2]==nums[1], skip!Letter Combinations of a Phone Number
Cartesion Product on Strings
Key Backtracking Logic
Decision Tree Width
Examples
Level I: Recursive Backtracking
Intuition
backtrack(idx, path), if idx == digits.length, add path to results. Otherwise, iterate through letters of digits[idx] and recurse.Detailed Dry Run
Dry Run: digits="2"
| idx | Digit | Letter | Path | Action |
| :-- | :---- | :----- | :--- | :----------------- |
| 0 | '2' | 'a' | "a" | Choose 'a', DFS(1) |
| 1 | - | - | "a" | Result! (Backtrack)|
| 0 | '2' | 'b' | "b" | Choose 'b', DFS(1) |
| 1 | - | - | "b" | Result! (Backtrack)|
| 0 | '2' | 'c' | "c" | Choose 'c', DFS(1) |Level II: Iterative BFS (Queue-based)
Intuition
Detailed Dry Run
- Queue: [""]
- Digit '2': Dequeue "", Enqueue "a", "b", "c". Queue: ["a", "b", "c"]
- Digit '3': Dequeue "a", Enqueue "ad", "ae", "af". Queue: ["b", "c", "ad", "ae", "af"]...
Level III: Optimized Backtracking (String Sharing / Buffer)
Intuition
StringBuilder (Java) or a fixed-size array and only convert to result strings at the leaves of the tree.Detailed Dry Run
Word Search
Backtracking on 2D Grids
Core Rules
- Use each cell at most once per word (requires tracking visited cells).
- Move in four directions: Up, Down, Left, Right.
Performance Optimization
Examples
Level I: DFS Backtracking (Visited Set)
Intuition
word[0], explore its neighbors recursively.visited set to keep track of used cells in the current path. If we match all characters of the word, return true.Detailed Dry Run
Dry Run: board=[['A','B'],['C','D']], word="ABD"
| Step | Cell | Char | Path | Action |
| :--- | :---: | :--- | :--- | :--------------- |
| 1 | (0,0) | 'A' | "A" | Match, try neighbors |
| 2 | (0,1) | 'B' | "AB" | Match, try neighbors |
| 3 | (1,1) | 'D' | "ABD"| Match! SUCCESS |
| 4 | (1,0) | 'C' | - | No Match (from A) |Level II: Optimized Backtracking (Frequency Pruning)
Intuition
Detailed Dry Run
N-Queens
The Constraint Logic
Efficient Collision Detection
- Column: Index
c - Main Diagonal: Index
r - c(constant along the diagonal) - Anti-Diagonal: Index
r + c(constant along the diagonal)
Examples
Level I: Backtracking with Validation Function
Intuition
r, try placing a queen at (r, c) where c from to . Check if (r, c) is safe by scanning all previously placed queens.Detailed Dry Run
Dry Run: N=4
| Row | Action | Collision Check |
| :-- | :--------------- | :-------------- |
| 0 | Place (0,0) | Valid |
| 1 | Try (1,0), (1,1) | ❌ Col/Diag |
| 1 | Place (1,2) | Valid |
| 2 | Try (2,0)..(2,3) | ❌ ALL COLLIDE |
| 1 | Backtrack (1,2) | Try (1,3)... |Level II: Optimized Backtracking (Hashing O(1) Checks)
Intuition
cols, diag1 (), and diag2 () sets. Before placing a queen at , check if , , or are in the sets.Detailed Dry Run
Level III: Bit Manipulation (Ultimate Speed)
Intuition
cols, ld (left-diagonal), and rd (right-diagonal) be integers. For each row, the available slots are ~(cols | ld | rd) & mask. Placing a queen update masks: cols | p, (ld | p) << 1, (rd | p) >> 1.Detailed Dry Run
Combinations
Choosing K from N
Efficient Search
[1, 2] and [2, 1], we always choose numbers in increasing order. This means after choosing , the next number must be from .Pruning Optimization
Examples
Level I: Recursive Backtracking (Include/Exclude)
Intuition
dfs(i + 1, count + 1), or we don't add and call dfs(i + 1, count). If count == k, we found a solution.Detailed Dry Run
Dry Run: n=3, k=2
| Start | Path | Action | Next State |
| :---- | :--- | :----------------- | :--------- |
| 1 | [] | Choose 1, dfs(2) | [1] |
| 2 | [1] | Choose 2, RES:[1,2]| - |
| 2 | [1] | Backtrack, try 3 | [1,3] |
| 3 | [1] | Choose 3, RES:[1,3]| - |
| 1 | [] | Skip 1, try Choose 2| [2] |Level II: Optimized Backtracking (Loop-based Pruning)
Intuition
Detailed Dry Run
- At root, i can only go up to . So we only try picking 1 or 2 as the first element.
Sudoku Solver
The Sudoku Logic
Backtracking Search
State Representation
Examples
Level I: Basic Backtracking
Intuition
'.', try numbers 1-9. Check if the number is valid in the current row, column, and box by scanning the board.Detailed Dry Run
Dry Run Trace:
| Cell | Value | Validity Check (R, C, B) | Action |
| :---: | :---: | :----------------------- | :----- |
| (0,2) | '4' | [✔, ✔, ✔] | Recurse|
| (0,3) | '1' | [❌ Row Conflict] | Try 2 |
| (0,3) | '6' | [✔, ✔, ✔] | Recurse|Level II: Optimized Backtracking (State Tracking with Bitmasks)
Intuition
rows[9], cols[9], and boxes[9] as integers. rows[i] & (1 << digit) tells us if digit is already in row i. This replaces the isValid check with bitwise .Detailed Dry Run
(rows[0] | cols[2] | boxes[0]) & (1 << 5) == 0.Palindrome Partitioning
Partitioning Strategy
The Decision Tree
s[start:end]. If it's a palindrome, we recurse on the remaining string s[end:].Optimization with DP
isPalindrome[i][j] using Dynamic Programming in to make each check .Examples
Level I: Standard Backtracking
Intuition
Detailed Dry Run
Dry Run: s="aab"
| Start | Substr | Is Pal? | Path | Action |
| :---- | :----- | :------ | :--- | :--- |
| 0 | "a" | YES | ["a"] | DFS(1) |
| 1 | "a" | YES | ["a","a"] | DFS(2) |
| 2 | "b" | YES | ["a","a","b"]| RES!! |
| 0 | "aa" | YES | ["aa"] | DFS(2) |
| 2 | "b" | YES | ["aa","b"] | RES!! |Level II: Backtracking with DP Precomputation
Intuition
dp[i][j] where dp[i][j] is true if s[i:j+1] is a palindrome. Use the recurrence: dp[i][j] = (s[i] == s[j] && (j - i <= 2 || dp[i+1][j-1])).Detailed Dry Run
N-Queens II
Counting Solutions
Optimization
Examples
Level I: Backtracking (Count only)
Intuition
count variable. Use boolean arrays to track occupied columns, main diagonals, and anti-diagonals.Detailed Dry Run
Dry Run: N=4
| Row | Valid Cols | Action | Count |
| :-- | :--- | :--- | :--- |
| 0 | {0,1,2,3} | Try 1 | 0 |
| 1 | {3} | Try 3 | 0 |
| 2 | {0} | Try 0 | 0 |
| 3 | {2} | FOUND! | 1 |Level II: Bitmask Backtracking
Intuition
Detailed Dry Run
Regular Expression Matching
The Matching Logic
- '.' matches any single character.
- '*' matches zero or more of the preceding element.
The Star (*) Dilemma
- Zero match: Skip the preceding element and the '*' (e.g.,
a*matches empty string). - One or more match: If the current character matches the preceding element, we consume one character of the string and stay at the same position in the pattern to potentially match more.
Examples
Level I: Recursive Backtracking (Naive)
Intuition
Detailed Dry Run
s="aa", p="a*"
| Step | s | p | First Match? | Action |
| :--- | :--- | :--- | :--- | :--- |
| 1 | "aa" | "a*" | YES | '*' detected, split |
| 2.1 | "aa" | "" | NO | Skip branch FAIL |
| 2.2 | "a" | "a*" | YES | Consume branch, recurse |
| 3.1 | "" | "a*" | YES | Consume branch, RES: true |Level II: Top-Down Memoization
Intuition
Detailed Dry Run
Cache entries for s="aa", p="a*":
(0, 0) -> calls (0, 2) and (1, 0)
(0, 2) -> true (base case)
(1, 0) -> calls (1, 2) and (2, 0)
... all stored in memo table.Level III: Bottom-Up Dynamic Programming
Intuition
dp[i][j] where dp[i][j] is true if s[i:] matches p[j:].dp[len(s)][len(p)] = true. Fill the table backwards from the base case.Detailed Dry Run
DP Table for s="aa", p="a*":
| | a | * | (end) |
|---|---|---|-------|
| a | T | T | F |
| a | T | T | F |
|(e)| F | T | T |Wildcard Matching
Matching Rules
- '?' matches any single character.
- '*' matches any sequence of characters (including empty sequence).
The Greedy '*' Logic
a* depends on 'a', the Wildcard * is independent. It can match anything. This makes it a great candidate for greedy search or dynamic programming.Examples
Level I: Recursive Backtracking (Naive)
Intuition
s="aaaaa", p="*a*a*a").Detailed Dry Run
s="aa", p="*"
| Step | s | p | Action |
| :--- | :--- | :--- | :--- |
| 1 | "aa" | "*" | '*' detected, branch |
| 2.1 | "aa" | "" | FAIL (skip *) |
| 2.2 | "a" | "*" | RECURSE (consume 'a') |
| 3.1 | "a" | "" | FAIL (skip *) |
| 3.2 | "" | "*" | SUCCESS (consume 'a', match empty) |Level II: Top-Down Memoization
Intuition
(i, j) match results to achieve complexity.Detailed Dry Run
For s="abc", p="*c":
memo[0][0] calls memo[0][1] and memo[1][0].
memo[1][0] calls memo[1][1] and memo[2][0].
Redundant paths like (*, c) matching 'bc' are cached.Level III: Optimized Greedy Path (Pointers)
Intuition
s and p indices. If we hit a mismatch later, reset p to right after '', and s to one char after the previous recorded s index.Detailed Dry Run
s="adceb", p="*a*b"
1. p[0]='*', star_idx=0, s_tmp=0. p moves to 1.
2. s[0]='a', p[1]='a'. Match! i=1, j=2.
3. p[2]='*', star_idx=2, s_tmp=1. p moves to 3.
4. s[1]='d', p[3]='b'. Mismatch! Backtrack to star_idx=2, s_tmp=2.
5. Keep trying until s[4]='b', p[3]='b'. MATCH!Partition to K Equal Sum Subsets
Filling the Buckets
TotalSum / K.Pruning Strategies (Crucial for Hard)
- If
TotalSumis not divisible by , return False immediately. - Sort
numsin descending order to fill buckets with larger numbers first (fails faster on impossible branches). - If a bucket cannot take the current number and it's the first number in the bucket, skip because further search will fail too.
Examples
Level I: Backtracking (Optimized with Sorting)
Intuition
bucket[j] + nums[idx] <= target, we add it and recurse. If we hit the end, success.Detailed Dry Run
nums=[5,4,3,2,1], target=5, k=3
1. Pick 5: Add to B1 (5). OK.
2. Pick 4: Add to B2 (4). OK.
3. Pick 3: B1(8)>5 (❌), B2(7)>5 (❌), B3(3). OK.
4. Pick 2: B1(5) (❌ skip), B2(6) (❌), B3(5). OK.Geometric Constraint
TotalLength / 4.Relation to K-Partition
Performance at Scale
Bitmask Optimization
Remove Invalid Parentheses
Minimal Removal
( and closed ) parentheses.BFS vs. Backtracking
- BFS: Guarantees finding the minimal removal level first. We remove one paren at a time and check validity.
- Backtracking: More memory efficient. We use the pre-calculated
remOpenandremClosedto limit our search space and prune invalid branches early.
Examples
Level I: Backtracking with Pruning
Intuition
remL and remR. In DFS, for each character: 1. If it's '(' and remL > 0, try removing it. 2. If it's ')' and remR > 0, try removing it. 3. Always try keeping it if valid.Detailed Dry Run
s="())", remL=0, remR=1
1. Index 0 '(': Keep it. (L=1, R=0)
2. Index 1 ')':
- Remove: DFS(idx 2, L=1, R=0) -> "()" Valid!
- Keep: (L=1, R=1)
3. Index 2 ')':
- Remove: DFS(idx 3, L=1, R=1) -> "()" Valid!Beautiful Arrangement
The divisibility Rule
perm[i] % i == 0ORi % perm[i] == 0
Pruning in Backtracking
Examples
Level I: Backtracking with Pruning
Intuition
visited array to track used numbers. At each position idx, iterate from 1 to N. If i is not used and satisfies the beautiful rule, place it and recurse.Detailed Dry Run
N=3
1. Pos 1: Try 1 (1%1==0). OK.
2. Pos 2: Try 2 (2%2==0). OK.
3. Pos 3: Try 3 (3%3==0). OK. (Result=1)
4. Pos 2: Try 3 (3%2!=0, 2%3!=0). Fail.Unique Paths III
The Grid Coverage Problem
Backtracking with Path Counting
- Count the total number of empty squares ().
- DFS from the start, marking cells as visited (-1).
- If we reach the end and our current path length equals (including the target), we found a valid path.
Examples
Level I: Backtracking (DFS)
Intuition
steps == totalEmpty, increment count.Detailed Dry Run
Trace for 1x3: [1, 0, 2]
1. Start at (0,0), empty=2.
2. Move to (0,1), steps=1. Mark visited.
3. Move to (0,2), steps=2. Target reached! Count++Hamiltonian Path
Visiting All Vertices
The NP-Complete Challenge
Examples
Level I: Backtracking with Visited Set
Intuition
Detailed Dry Run
N=3. Edges: 0-1, 1-2
1. Start at 0: Visit 0. Next: 1. Visit 1. Next: 2. Visit 2. Length=3. DONE!Course4All Technical Board
Verified ExpertSenior Software Engineers & Algorithmic Experts
Our DSA content is authored and reviewed by engineers from top tech firms to ensure optimal time and space complexity analysis.
Found an issue or have a suggestion?
Help us improve! Report bugs or suggest new features on our Telegram group.