Master this topic with zero to advance depth.
Combination Sum
Given an array of distinct integers candidates and a target integer, find all unique combinations 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.
At each step, you can either:
Find all unique combinations in candidates that sum to target (reuse allowed). Includes visual decision tree.
Examples
Intuition
At every step, we either include the current candidate or move to the next candidate.
We use recursion to explore the decision tree. If 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 |Intuition
Sorting the candidates allows us to break early from the loop once the target is exceeded.
For each call, iterate through candidates from the current index to the end. If target - candidates[i] < 0, stop the loop (pruning).
Detailed Dry Run
target=7, candidates=[2,3,6,7]
Intuition
Since we only care about the values and can reuse items, we can use a DP table where dp[i] stores all unique combinations summing to i.
Initialize 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
target=3, candidates=[1,2]
Permutations
In permutation problems, the goal is to arrange all elements of a set in every possible order. Unlike combinations, the order matters here ([1, 2] is different from [2, 1]).
Given an array nums of distinct integers, return all possible permutations. Includes visual state-space tree.
Examples
Intuition
Maintain a list of 'remaining' elements or a boolean array to track usage.
Iterate through all numbers. If a number is not visited, add it to the current permutation, mark it visited, and recurse. Unmark and remove after recursion.
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!) |Intuition
Instead of a visited array, we can rearrange the input array itself to represent the current state.
At index 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
nums=[1,2,3], first=0
Intuition
Generate permutations in lexicographical order. This is useful when the input is sorted or we need to find the 'next' permutation.
Start with the smallest permutation (sorted). Repeatedly find the next lexicographical permutation until we return to the start.
Detailed Dry Run
nums=[1,2,3]
Subsets
A power set of is the set of all subsets of , including the empty set and itself. For a set with elements, there are subsets.
At each step, we have two choices for the current element: either include it in the current subset or exclude it.
Generate all possible subsets (the power set) of a set of unique integers. Includes visual inclusion-exclusion trace.
Examples
Intuition
At each step, we decide whether to add nums[i] to our subset or not.
Use a recursive function 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: []|Intuition
Start with an empty set and gradually build the power set.
Start with [[]]. For each number in nums, duplicate all existing subsets and add the current number to the duplicates.
Detailed Dry Run
nums=[1,2]
Intuition
Each subset of a set with elements can be uniquely mapped to a binary number from to .
Loop from 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
nums=[1,2]
Subsets II
When the input array contains duplicates, the standard power set logic generates duplicate subsets. To avoid this, we must sort the array and skip identical elements during the recursion.
If 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.
Generate all possible subsets of an integer array that may contain duplicates. Includes visual duplicate-skipping trace.
Examples
Intuition
Sort and skip duplicates to ensure uniqueness.
Sort the array first. In the loop-based backtracking, if 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
This problem asks for the Cartesian product of several sets of characters. Each digit from 2-9 maps to a set of letters (like a telephone keypad).
We iterate through the input digits. For the current digit, we try all mapped letters, appending them to our current path, and moving to the next digit.
The width of the tree varies: 3 for most digits (2, 3, 4, 5, 6, 8), and 4 for digits '7' (pqrs) and '9' (wxyz).
Find all letter combinations for a phone number. Includes visual Cartesian product tree.
Examples
Intuition
Treat the problem as a tree traversal where each digit is a level and each letter is a branch.
Maintain a mapping of digits to letters. At each call 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) |Intuition
Instead of recursion, use a queue to store intermediate combinations.
Initialize queue with an empty string. For each digit, dequeue all strings, append each possible letter for that digit, and enqueue the new strings.
Detailed Dry Run
digits="23"
Intuition
In some languages, creating many small strings is expensive. Using a shared buffer can improve performance.
Use a StringBuilder (Java) or a fixed-size array and only convert to result strings at the leaves of the tree.
Detailed Dry Run
Same as Level I, but focusing on memory alloc efficiency.
Word Search
This problem is a classic example of DFS on a grid. We need to find a sequence of adjacent cells that match the characters of a given word.
To avoid using extra space for a visited array, we can temporarily modify the board with a special character (like '#') to mark a cell as visited, then restore it after recursion (Backtracking).
Find a word in a 2D grid using DFS. Includes visual traversal trace.
Examples
Intuition
Try starting at every cell . If it matches word[0], explore its neighbors recursively.
Use a 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) |Intuition
Before starting DFS, check if the board contains enough characters to form the word.
Count the frequency of each char in the board and the word. If the board has fewer characters than the word for any char, return false immediately. This avoids work in impossible cases.
Detailed Dry Run
word="AAAAA", board has only two 'A's -> Return FALSE immediately.
N-Queens
The N-Queens puzzle is about placing chess queens on an chessboard so that no two queens threaten each other. A queen can attack in 3 ways: Row, Column, and Diagonal.
Instead of checking every cell on the board (which is ), we can use boolean arrays to mark occupied lanes:
cr - c (constant along the diagonal)r + c (constant along the diagonal)Place N queens on an N x N board without conflicts. Includes visual 4-Queens solution.
Examples
Intuition
Place queens row by row. For each valid column in a row, move to the next row.
For each row 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)... |Intuition
Use boolean arrays/sets to track occupied columns and diagonals for immediate collision detection.
Maintain cols, diag1 (), and diag2 () sets. Before placing a queen at , check if , , or are in the sets.
Detailed Dry Run
Place (0,0): cols={0}, d1={0}, d2={0} Row 1: (1,1) fails (d1), (1,2) ok (c=2, d1=-1, d2=3).
Intuition
Use integers as bitmasks to represent occupied columns and diagonals. This is the fastest way because bitwise operations are extremely cheap.
Let 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
N=4, mask=1111 Row 0: Try p=0001. New cols=0001, ld=0010, rd=0000 Row 1: Try p=0100...
Combinations
This is a classic problem. The goal is to choose elements from the set . Unlike permutations, the order doesn't matter.
To avoid duplicate combinations like [1, 2] and [2, 1], we always choose numbers in increasing order. This means after choosing , the next number must be from .
If the number of remaining elements in the set is less than the number of elements we still need to pick, we can backtrack early.
Choose K numbers from 1 to N. Includes visual increasing-order tree.
Examples
Intuition
For each number from 1 to , we either include it in our current combination or skip it.
At index , we either add to our list and call 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] |Intuition
Instead of binary choice, use a loop to pick the next element. Only iterate through elements that could possibly complete a combination of size .
The current number can go up to . Beyond that, there aren't enough numbers left to reach size .
Detailed Dry Run
n=4, k=3
Sudoku Solver
Sudoku is a constraint satisfaction problem. A valid solution must have the digits 1-9 exactly once in every row, column, and subgrid.
We find an empty cell, try placing a digit from 1-9, and if it's valid, move to the next empty cell. If no digit works, we backtrack to the previous cell and try the next possible digit.
Instead of searching the board to validate, we can use boolean matrices or bitmasks to track which numbers are present in each row, column, and box.
Solve a Sudoku puzzle using backtracking. Includes visual constraint check trace.
Examples
Intuition
Try every number in every empty cell.
Iterate through cells. For each '.', 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|Intuition
Use bitmasks to mark digits used in rows, columns, and boxes for validation.
Maintain 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
To check if '5' can be placed at (0, 2):
Check: (rows[0] | cols[2] | boxes[0]) & (1 << 5) == 0.
Palindrome Partitioning
A string can be partitioned in many ways. For Palindrome Partitioning, we only care about partitions where every substring is a palindrome.
At each step, we pick a substring s[start:end]. If it's a palindrome, we recurse on the remaining string s[end:].
Checking if a substring is a palindrome repeatedly is . We can precompute a 2D boolean array isPalindrome[i][j] using Dynamic Programming in to make each check .
Partition a string into all possible palindromic substrings. Includes visual cut-tree.
Examples
Intuition
Try all possible prefixes. If a prefix is a palindrome, recurse on the suffix.
Iterate through the string. At each position, try taking a substring from the start to the current position. If it's a palindrome, add it to the path and recurse.
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!! |Intuition
Avoid repetitive palindrome checks by precomputing all possible palindromes in the string.
Create a 2D boolean array 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
s="aab" dp[0][0]=T, dp[1][1]=T, dp[2][2]=T dp[0][1] = (s[0]==s[1]) = T dp[1][2] = (s[1]==s[2]) = F dp[0][2] = (s[0]==s[2] && dp[1][1]) = F
N-Queens II
This is identical to N-Queens I, but instead of returning the board layouts, we only need to return the total number of distinct solutions.
Since we don't need to construct the board, we can use bitmasks for extremely fast collision detection and state management.
Count the number of distinct N-Queens solutions. Includes visual N=4 solution count.
Examples
Intuition
Use the same backtracking logic as N-Queens I, but increment a counter instead of saving the board.
Maintain a 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 |Intuition
Use integers as bitmasks for lightning-fast state management.
Represent occupied columns, diagonals as bits in an integer. Only use bitwise operations to check and update state.
Detailed Dry Run
N=4. Initial: columns=0, ld=0, rd=0. Available: bits in ~(cols | ld | rd).
Regular Expression Matching
Implement regular expression matching with support for '.' and '*'.
The '' is the most complex part of this problem. When we encounter a '', we have two choices:
a* matches empty string).Implement regex matching with support for '.' and '*'. Includes visual state-space analysis.
Examples
Intuition
Handle the base match, then branch for the '*' character.
For each call, check if the first character matches. If the second character of the pattern is '*', branch into 'skip' or 'consume' cases.
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 |Intuition
Cache results of (i, j) pairs where i is index in s and j is index in p.
The naive recursion has overlapping subproblems. By storing results in a 2D array/hashmap, we reduce complexity to .
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.Intuition
Build a table dp[i][j] where dp[i][j] is true if s[i:] matches p[j:].
Initialize 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
Unlike Regular Expression matching where a* depends on 'a', the Wildcard * is independent. It can match anything. This makes it a great candidate for greedy search or dynamic programming.
Implement wildcard pattern matching with support for '?' and '*'. Includes visual matching trace.
Examples
Intuition
Traverse both strings. If we hit '?', match one. If we hit '*', try matching zero or more characters.
Standard recursion with branching on '*'. This is in the worst case (e.g., 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) |Intuition
Overlap in recursive branches for '*' leads to redundant work.
Store (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.Intuition
Instead of a full DP table, we can track the last '*' position and backtrack only as much as needed.
When we hit '', record the current 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
We need to divide numbers into groups, where each group has the same sum. This target sum is TotalSum / K.
TotalSum is not divisible by , return False immediately.nums in descending order to fill buckets with larger numbers first (fails faster on impossible branches).Partition an array into K subsets of equal sum. Includes visual bucket-filling trace.
Examples
Intuition
Try putting each number into each of the K buckets.
To optimize, we sort descending. In each step, we iterate through K buckets. If 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.To form a square, we must partition the matchsticks into 4 groups of equal length. This length must be TotalLength / 4.
This is a special case of Partition to K Equal Sum Subsets where . The same pruning strategies (descending sort, skip empty buckets) apply here for efficiency.
While basic backtracking works for Sudoku, larger puzzles or high-volume solvers require better state management.
Instead of checking rows and columns using loops (), we use a single bitmask for each row, column, and box. Checking for a conflict becomes a single bitwise AND operation ().
Remove Invalid Parentheses
To make a string valid with minimal removals, we first calculate the number of misplaced open ( and closed ) parentheses.
remOpen and remClosed to limit our search space and prune invalid branches early.Remove the minimum number of invalid parentheses to make the input string valid. Includes visual removal tree.
Examples
Intuition
Count how many '(' and ')' need to be removed, then use DFS to try all removal combinations.
Calculate 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
An arrangement is beautiful if for every position (1-indexed):
perm[i] % i == 0 ORi % perm[i] == 0Instead of generating all permutations and checking them, we check the condition at each step. If a number cannot be placed at the current index, we prune the entire branch.
Count the number of beautiful arrangements of numbers 1 to N. Includes visual placement trace.
Examples
Intuition
Try placing each available number in the current position, checking the rule.
Use a 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
Find the number of paths from the starting square to the ending square that walk over every non-obstacle square exactly once.
Find the number of paths in a grid that visit every empty square exactly once. Includes visual grid-coverage trace.
Examples
Intuition
Try all possible paths from start to end, counting steps taken.
Iterate through the grid to find the start and count empty cells. DFS in 4 directions. If we reach '2' and 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
A Hamiltonian Path is a path in an undirected or directed graph that visits every vertex exactly once.
This is a classic NP-complete problem. Unlike Eulerian paths (which visit every edge), there is no simple polynomial-time check for Hamiltonian paths.
Determine if a graph contains a path that visits every node exactly once. Includes visual node-visit trace.
Examples
Intuition
Pick a starting node and try to build a path of length N.
Try every node as a starting vertex. Perform DFS, keeping track of visited nodes. If the path length reaches , we found a Hamiltonian Path.
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!Help us improve! Report bugs or suggest new features on our Telegram group.