Hi👋SpeedAlgo • Deliberate Practice & Cognitive Ergonomics for Software Engineers

An interactive algorithm mastery and technical interview preparation platform published by Hi👋WebEnterprise. Built for senior and staff software engineers preparing for rigorous coding screens at top tech companies (FAANG/MAMAA).

13 Core Algorithmic Patterns & 225 Practice Problems

  • 1. Two Pointers (16 Paradigms, 43 Problems): Opposite ends, sorted pair sums, palindrome verification, trapping rain water, 3Sum, container with most water — includes the Sliding Window and Fast & Slow Pointers paradigms (contiguous subarray invariants, longest substrings, Floyd's cycle detection, linked list middle).
  • 2. Binary Search (9 Paradigms, 14 Problems): Monotonic predicate partitioning (feasible(x)), boundary search, rotated sorted arrays, square roots, minimum ship capacity, median of two sorted arrays, matrix median on value range.
  • 3. Bit Manipulation (5 Paradigms, 11 Problems): Counting set bits, XOR cancellation, bit reversal, bitmask pairing, maximum XOR with a bitwise trie.
  • 4. Math & Geometry (8 Paradigms, 14 Problems): Sieve of Eratosthenes, integer/roman conversions, modular arithmetic, geometric simulation.
  • 5. Tree/Graph Depth-First Search (15 Paradigms, 24 Problems): Path sums, lowest common ancestor, tree diameter, validating BSTs, tree DP (House Robber III, Binary Tree Cameras).
  • 6. Tree/Graph Breadth-First Search (5 Paradigms, 12 Problems): Level-order traversals, shortest path in unweighted graphs, zig-zag traversals, rotting oranges, word ladders.
  • 7. Graphs (12 Paradigms, 22 Problems): Topological sort, cycle detection, Dijkstra's algorithm, bipartite graph validation, network delay.
  • 8. Backtracking (6 Paradigms, 14 Problems): Subsets, permutations, combinations, constraint satisfaction, pruning, N-Queens, Sudoku solver.
  • 9. Dynamic Programming (13 Paradigms, 28 Problems): Memoization vs tabulation, 0/1 knapsack, unbounded knapsack, coin change, edit distance, longest common subsequence.
  • 10. Heap / Priority Queue (9 Paradigms, 11 Problems): Running medians, top-k elements, interval scheduling, IPO, k-way merges.
  • 11. Advanced Data Structures (7 Paradigms, 17 Problems): Trie (prefix tree), Union-Find (Disjoint Set Union), LRU Cache, LFU Cache, range-sum queries with updates.
  • 12. Stack (5 Paradigms, 9 Problems): Next greater element, largest rectangle in histogram, remove k digits, decode string, min stack.
  • 13. Intervals & Greedy (2 Paradigms, 6 Problems): Merge intervals, interval scheduling, meeting rooms by sweep line, jump game.

4-Stage Deliberate Practice Framework

  1. Stage 1 (Compare & Learn): Read the pattern's mental model, loop invariant and Big-O proof, compare the pattern's template with the problem's solution side by side, and use the step-by-step debugger. The solution can also be shown in C#, Java, TypeScript, C++, Go or Rust for almost every problem.
  2. Stage 2 (Active Recall): Rebuild the solution from memory in a code editor, with an invariant checklist and optional progressive hints, and check it against a judged test suite.
  3. Stage 3 (AI Interview Coach, coming soon): A mock-interview stage still in development; for now, students go straight from Stage 2 to Stage 4.
  4. Stage 4 (Solve on Your Own): Solve a new problem on your own against a 15-minute timer; your code is graded against a test suite. Python and TypeScript run in your browser and C# runs on our server (design problems grade in Python and TypeScript).

Equipped with a spaced-repetition review hub on a fixed 7-step schedule (1 day up to 90 days), a two-pane Studio workspace, and study notes.

Pricing, Access & Commercial Terms

  • Core Curriculum: 100% Free. No credit card required.
  • Coins: New accounts get 40 free coins once, the daily claim adds +20 coins once a day, and inviting a friend earns you +25 coins when they complete their first practice step. These are the only ways to earn coins.
  • AI Coach: Each AI coach question costs 1-5 coins, priced by the server according to its size.
  • BYOK (Bring Your Own Key): AI coaching is free with your own Google Gemini, OpenAI or OpenRouter API key (BYOK).
  • Extras: Coins also unlock extras for 24 hours: a Recall hint level (1 coin), a Solve solution reveal (2), a LeetCode problem card's solutions (1) and a pattern's extra canonical problems (1).
  • Payments & Refunds: Nothing on the site is sold for money: coins can only be earned, never bought. There are no subscriptions or charges, so there is nothing to refund.
  • Platform Operator: Hi👋WebEnterprise. Support & policies at hispeedalgo.com.
Skip to main content
Hi👋SpeedAlgo

Invariant-First Algorithmic Mastery

Arena
238Items
Theory Context•Backtracking & State Search
MediumLC 39

Combination Sum (LeetCode 39)

Target Frequency:AmazonGoogleMeta

You get an array of distinct positive integers candidates and a target integer target. Find every distinct combination of numbers from candidates that adds up exactly to target, and return them in any order.

A single candidate may be reused as many times as you like within one combination, so two combinations only count as different when they use a different count of at least one number.

Inputs are chosen so that fewer than 150 valid combinations exist for the given candidates and target.

Worked Examples

Example 1
Input:candidates = [2,3,6,7], target = 7
Output:[[2,2,3],[7]]
Explanation: Reusing candidate 2 twice alongside 3 gives 2 + 2 + 3 = 7, and taking 7 by itself also totals 7 -- no other combination of these candidates reaches the target.
Example 2
Input:candidates = [2,3,5], target = 8
Output:[[2,2,2,2],[2,3,3],[3,5]]
Explanation: 2+2+2+2, 2+3+3 and 3+5 all make 8; a number may be used more than once.
Example 3
Input:candidates = [2], target = 1
Output:[]
Explanation: Only 2s are available, and no number of 2s adds up to 1.

⚖️Formal Constraints & Bounds

  • 1 <= candidates.length <= 30

  • 2 <= candidates[i] <= 40

  • All elements of candidates are distinct.

  • 1 <= target <= 40

Deep-Dive & Conceptual Insights

Core Insight

Passing the current index i (not i + 1) back into the recursive call is what allows a candidate to be reused. Branches are cut when a candidate exceeds the remaining target -- a genuine feasibility prune -- and sorting first means the first overshooting candidate proves every later one also overshoots, so the loop can break instead of continue.

Real-World Scenario & Production Applications

Making exact change with unlimited coins of each denomination, or finding every way to combine a fixed set of package sizes to hit an exact shipping weight, both require the same reuse-allowed combination search.

Subproblems & Recurrence Decomposition3 Phases

🧩Subproblem 1: Target Remainder Reduction & Sorting Pruning

Sort candidates and iterate from start index. Prune branches early whenever candidates[i] > remain.

Mathematical Recurrence / Code Invariant
for i in range(start, len(candidates)):
    if candidates[i] > remain:
        break  # Prune exceeding candidates

Step-by-Step Execution Trace Table

Code / Blueprint
[ ] (rem=7, start=0, d=0)
/ \ \ \
choose 2 / choose 3\ choose 6 \ choose 7\
/ \ \ \
[ 2 ] (rem=5, d=1)[ 3 ] (rem=4, d=1)[ 6 ] (rem=1, d=1)[ 7 ] (rem=0, d=1)
/ \ \ | ✅ RECORD [7]
choose 2/ choose 3\ choose 3 \ 6>1 ✂️ PRUNE
/ \ \
[2,2] (rem=3, d=2)[2,3] (rem=2)[3,3] (rem=1)
/ \ | |
choose 2\ choose 3\ 3>2 ✂️ PRUNE 3>1 ✂️ PRUNE
[2,2,2] [2,2,3] (d=3)
(rem=1) (rem=0) ✅ RECORD [2,2,3]
|
2>1 ✂️ PRUNE
 
Step-by-Step Decision Trace
  1. Step 1 (Root, d=0, rem=7): Candidates: [2, 3, 6, 7].
  2. Step 2 (Choose 2, d=1, rem=5): path = [2]. Pass index i=0 to allow element reuse.
  3. Step 3 (Choose 2, d=2, rem=3): path = [2, 2]. Pass index i=0.
  4. Step 4 (Choose 2, d=3, rem=1): path = [2, 2, 2]. Next candidate 2 > 1 →\rightarrow→ ✂️ PRUNE & BREAK.
  5. Step 5 (Backtrack / Pop 2, d=2): path = [2, 2], rem = 3. Next candidate: 3.
  6. Step 6 (Choose 3, d=3, rem=0): path = [2, 2, 3]. rem == 0 Target Hit →\rightarrow→ Record [2, 2, 3] ✅.
  7. Step 7 (Backtrack / Pop 3, d=2): path = [2, 2]. Next candidate 6 > 3 →\rightarrow→ ✂️ PRUNE & BREAK.
  8. Step 8 (Backtrack / Pop 2, d=1): path = [2], rem = 5. Next candidate: 3.
  9. Step 9 (Choose 3, d=2, rem=2): path = [2, 3]. Next candidate 3 > 2 →\rightarrow→ ✂️ PRUNE & BREAK.
  10. Step 10 (Backtrack to Root, d=0): Next candidate at root is 3.
  11. Step 11 (Choose 3, d=1, rem=4): path = [3]. Reuse 3 →\rightarrow→ path = [3, 3] (rem=1). Next 3 > 1 →\rightarrow→ ✂️ PRUNE.
  12. Step 12 (Root Candidate 6, d=1): path = [6] (rem=1). Next 6 > 1 →\rightarrow→ ✂️ PRUNE.
  13. Step 13 (Root Candidate 7, d=1): path = [7] (rem=0). Target Hit →\rightarrow→ Record [7] ✅. Search Complete: Output [[2, 2, 3], [7]].
Full Walkthrough13 Steps
Input:candidates = [2,3,6,7], target = 7Expected:[[2,2,3],[7]]
1🌱 ROOT(Root, d=0, rem=7)
remaining = 7
Choices:2 3 6 7
Candidates: [2, 3, 6, 7].
2🟢 CHOOSE(Choose 2, d=1, rem=5)
path = [2]
path = [2]. Pass index i=0 to allow element reuse.
3🟢 CHOOSE(Choose 2, d=2, rem=3)
path = [2, 2]
path = [2, 2]. Pass index i=0.
4✂️ PRUNED(Choose 2, d=3, rem=1)
path = [2, 2, 2]
path = [2, 2, 2]. Next candidate 2 > 1 →\rightarrow→ ✂️ PRUNE & BREAK.
5↩️ BACKTRACK / POP(Backtrack / Pop 2, d=2)
path = [2, 2]
path = [2, 2], rem = 3. Next candidate: 3.
6✅ RECORD / GOAL(Choose 3, d=3, rem=0)
path = [2, 2, 3]
path = [2, 2, 3]. rem == 0 Target Hit →\rightarrow→ Record [2, 2, 3] ✅.
7✂️ PRUNED(Backtrack / Pop 3, d=2)
path = [2, 2]
path = [2, 2]. Next candidate 6 > 3 →\rightarrow→ ✂️ PRUNE & BREAK.
8↩️ BACKTRACK / POP(Backtrack / Pop 2, d=1)
path = [2]
path = [2], rem = 5. Next candidate: 3.
9✂️ PRUNED(Choose 3, d=2, rem=2)
path = [2, 3]
path = [2, 3]. Next candidate 3 > 2 →\rightarrow→ ✂️ PRUNE & BREAK.
10🌱 ROOT(Backtrack to Root, d=0)
Next candidate at root is 3.
11✂️ PRUNED(Choose 3, d=1, rem=4)
path = [3]
path = [3]. Reuse 3 →\rightarrow→ path = [3, 3] (rem=1). Next 3 > 1 →\rightarrow→ ✂️ PRUNE.
12🌱 ROOT(Root Candidate 6, d=1)
path = [6]
path = [6] (rem=1). Next 6 > 1 →\rightarrow→ ✂️ PRUNE.
13🌱 ROOT(Root Candidate 7, d=1)
path = [7]
path = [7] (rem=0). Target Hit →\rightarrow→ Record [7] ✅. Search Complete: Output [[2, 2, 3], [7]].
Code Shape
Archetype Code Shape
Python
1candidates.sort(), results = [], path = []
2def dfs(start, remaining):
3 if remaining == 0:
4 results.append(list(path))
5 return
6 for i in range(start, len(candidates)):
7 if candidates[i] > remaining:
8 break
9 path.append(candidates[i])
10 dfs(i, remaining - candidates[i])
11 path.pop()
12dfs(0, target)
13return results

Conceptual Narrative

🧭 Conceptual Foundation & Pattern Intuition

Every earlier archetype recursed on i + 1, permanently retiring an element once chosen. Here, elements can be reused unlimited times — and the fix is a one-character change with a big conceptual meaning.

The Analogy: An Unlimited Supply of Coin Denominations

Think of this like making change with coins, but with an unlimited supply of every denomination. After choosing to use a $2 coin, you're not forbidden from using another $2 coin next — the coin type stays available for reuse. What does stay forbidden is going back to a $1 coin after you've moved on to considering $2s and larger, since that would just generate the same combination in a different order ([1,2] and [2,1] are the same combination of coins, unlike with permutations).

🔑 The Key Idea in the Code

dfs(i, ...) instead of dfs(i + 1, ...) is the entire trick: the current candidate remains a legal choice for the very next recursive call, while candidates before i are still permanently excluded — giving reuse of the current element without ever re-allowing an earlier, already-passed-over one.

  • Advancing index i + 1 instead of i: Because elements can be reused unlimited times, the recursive call must pass i (not i + 1) to allow the same element to be chosen again.

  • Exploring candidates when current_sum + num > target: Sorting candidates upfront allows an immediate break from the loop as soon as candidates[i] > remaining, pruning entire subtrees.

  • Restarting loop at 0 instead of start: Starting the candidate loop at 0 generates permutations instead of combinations (e.g. [2, 3] and [3, 2]). Always start loop at start index.

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

"Return all combinations summing to target" + explicit unlimited reuse of the same value + distinct candidates.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

Invariant: remaining is always target minus the sum of path. Complete when remaining == 0. A candidate c is prunable the moment c > remaining, and because candidates are sorted, every later candidate is prunable too.

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

  • Passing i + 1 (forbids reuse, becomes Combination Sum II's shape) when the problem explicitly allows reuse -- must pass i.

  • Using continue instead of break on overshoot -- correct either way, but break is the O(N^(T/m))-improving optimisation sorted input licenses, and an interviewer will ask why.

  • Forgetting to sort first -- without it, the overshoot prune cannot safely break, since a later unsorted candidate might still be small enough.

The 60-Second Interview Pitch

Say this out loud before you type a single line

"I sort candidates first, then DFS with (start_index, remaining). If remaining == 0 I record a copy of path. Otherwise I loop from start_index: if candidates[i] > remaining I break, since sorting guarantees every later candidate also overshoots. Otherwise I append it, recurse with (i, remaining - candidates[i]) -- passing i, not i + 1, to allow reuse -- then pop. Time is roughly O(N^(T/m)) where m is the smallest candidate."

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(N^(T/M))

Look at the execution steps: Let T = target, M = min(candidates), N = len(candidates). Maximum tree depth is T / M. In the worst case, branching factor is N, giving O(N^(T/M)) operations.

SPACE COMPLEXITY

O(T/M) Call Stack

Recursion stack depth <= T / M; path buffer size <= T / M.

Formal Recurrence Relation

T(N, T) = O(N^{T / min(C)})

Derivation Progression

Tree Depth Bound

Max Depth = floor(target / min(candidates))

Recursion cannot descend deeper than target divided by smallest coin.

Branching Factor

N choices per state

Can reuse current element or move to next candidate index.

Over-Target Pruning

if remain < 0: return

Immediately terminates branches exceeding target sum.

Variable Definitions

NNN

Number of candidate values

TTT

Target remainder sum

MMM

Minimum candidate value in the array

Memory Architecture & Bounds

🟣 Call Stack

O(T / M) Call stack depth

🔵 Auxiliary Heap

O(T / M) Current combination path

🟢 Output Space

O(K · D) Result combinations

Boundary Best / Worst Cases

Best Case

O(1)O(1)O(1) if single candidate equals target.

Average Case

O(NT/M)O(N^{T/M})O(NT/M).

Worst Case

O(NT/M)O(N^{T/M})O(NT/M) with small candidate values.

Decision & State-Space Search Tree

Decision & State-Space Search Tree
Synthesizing vector architecture diagram...
Staff+ Engineering Perspective

Senior SWE Deconstruction & Hardware Caveats

ARCHITECTURAL SIGNALS & INTERVIEW TRIGGERS

Triggers: "candidates sum to target", "same number chosen unlimited times". Index persistence: when candidate iii is selected, recurse with index iii (not i+1i + 1i+1) to allow repeated selection of the same element while preventing duplicate combinations in different orders.

CONSTRAINTS & BOUNDS

candidates≤30,target≤40candidates \le 30, target \le 40candidates≤30,target≤40. Sorting candidates upfront enables early loop termination when candidates[i]>remainingcandidates[i] > remainingcandidates[i]>remaining.

FAANG PRODUCTION TRAPS & EDGE CASES

Target exceeds zero: prune immediately if remaining<0remaining < 0remaining<0.

Theory Context•Backtracking & State Search
MediumLC 39

Combination Sum (LeetCode 39)

Target Frequency:AmazonGoogleMeta

You get an array of distinct positive integers candidates and a target integer target. Find every distinct combination of numbers from candidates that adds up exactly to target, and return them in any order.

A single candidate may be reused as many times as you like within one combination, so two combinations only count as different when they use a different count of at least one number.

Inputs are chosen so that fewer than 150 valid combinations exist for the given candidates and target.

Worked Examples

Example 1
Input:candidates = [2,3,6,7], target = 7
Output:[[2,2,3],[7]]
Explanation: Reusing candidate 2 twice alongside 3 gives 2 + 2 + 3 = 7, and taking 7 by itself also totals 7 -- no other combination of these candidates reaches the target.
Example 2
Input:candidates = [2,3,5], target = 8
Output:[[2,2,2,2],[2,3,3],[3,5]]
Explanation: 2+2+2+2, 2+3+3 and 3+5 all make 8; a number may be used more than once.
Example 3
Input:candidates = [2], target = 1
Output:[]
Explanation: Only 2s are available, and no number of 2s adds up to 1.

⚖️Formal Constraints & Bounds

  • 1 <= candidates.length <= 30

  • 2 <= candidates[i] <= 40

  • All elements of candidates are distinct.

  • 1 <= target <= 40

Deep-Dive & Conceptual Insights

Core Insight

Passing the current index i (not i + 1) back into the recursive call is what allows a candidate to be reused. Branches are cut when a candidate exceeds the remaining target -- a genuine feasibility prune -- and sorting first means the first overshooting candidate proves every later one also overshoots, so the loop can break instead of continue.

Real-World Scenario & Production Applications

Making exact change with unlimited coins of each denomination, or finding every way to combine a fixed set of package sizes to hit an exact shipping weight, both require the same reuse-allowed combination search.

Subproblems & Recurrence Decomposition3 Phases

🧩Subproblem 1: Target Remainder Reduction & Sorting Pruning

Sort candidates and iterate from start index. Prune branches early whenever candidates[i] > remain.

Mathematical Recurrence / Code Invariant
for i in range(start, len(candidates)):
    if candidates[i] > remain:
        break  # Prune exceeding candidates

Step-by-Step Execution Trace Table

Code / Blueprint
[ ] (rem=7, start=0, d=0)
/ \ \ \
choose 2 / choose 3\ choose 6 \ choose 7\
/ \ \ \
[ 2 ] (rem=5, d=1)[ 3 ] (rem=4, d=1)[ 6 ] (rem=1, d=1)[ 7 ] (rem=0, d=1)
/ \ \ | ✅ RECORD [7]
choose 2/ choose 3\ choose 3 \ 6>1 ✂️ PRUNE
/ \ \
[2,2] (rem=3, d=2)[2,3] (rem=2)[3,3] (rem=1)
/ \ | |
choose 2\ choose 3\ 3>2 ✂️ PRUNE 3>1 ✂️ PRUNE
[2,2,2] [2,2,3] (d=3)
(rem=1) (rem=0) ✅ RECORD [2,2,3]
|
2>1 ✂️ PRUNE
 
Step-by-Step Decision Trace
  1. Step 1 (Root, d=0, rem=7): Candidates: [2, 3, 6, 7].
  2. Step 2 (Choose 2, d=1, rem=5): path = [2]. Pass index i=0 to allow element reuse.
  3. Step 3 (Choose 2, d=2, rem=3): path = [2, 2]. Pass index i=0.
  4. Step 4 (Choose 2, d=3, rem=1): path = [2, 2, 2]. Next candidate 2 > 1 →\rightarrow→ ✂️ PRUNE & BREAK.
  5. Step 5 (Backtrack / Pop 2, d=2): path = [2, 2], rem = 3. Next candidate: 3.
  6. Step 6 (Choose 3, d=3, rem=0): path = [2, 2, 3]. rem == 0 Target Hit →\rightarrow→ Record [2, 2, 3] ✅.
  7. Step 7 (Backtrack / Pop 3, d=2): path = [2, 2]. Next candidate 6 > 3 →\rightarrow→ ✂️ PRUNE & BREAK.
  8. Step 8 (Backtrack / Pop 2, d=1): path = [2], rem = 5. Next candidate: 3.
  9. Step 9 (Choose 3, d=2, rem=2): path = [2, 3]. Next candidate 3 > 2 →\rightarrow→ ✂️ PRUNE & BREAK.
  10. Step 10 (Backtrack to Root, d=0): Next candidate at root is 3.
  11. Step 11 (Choose 3, d=1, rem=4): path = [3]. Reuse 3 →\rightarrow→ path = [3, 3] (rem=1). Next 3 > 1 →\rightarrow→ ✂️ PRUNE.
  12. Step 12 (Root Candidate 6, d=1): path = [6] (rem=1). Next 6 > 1 →\rightarrow→ ✂️ PRUNE.
  13. Step 13 (Root Candidate 7, d=1): path = [7] (rem=0). Target Hit →\rightarrow→ Record [7] ✅. Search Complete: Output [[2, 2, 3], [7]].
Full Walkthrough13 Steps
Input:candidates = [2,3,6,7], target = 7Expected:[[2,2,3],[7]]
1🌱 ROOT(Root, d=0, rem=7)
remaining = 7
Choices:2 3 6 7
Candidates: [2, 3, 6, 7].
2🟢 CHOOSE(Choose 2, d=1, rem=5)
path = [2]
path = [2]. Pass index i=0 to allow element reuse.
3🟢 CHOOSE(Choose 2, d=2, rem=3)
path = [2, 2]
path = [2, 2]. Pass index i=0.
4✂️ PRUNED(Choose 2, d=3, rem=1)
path = [2, 2, 2]
path = [2, 2, 2]. Next candidate 2 > 1 →\rightarrow→ ✂️ PRUNE & BREAK.
5↩️ BACKTRACK / POP(Backtrack / Pop 2, d=2)
path = [2, 2]
path = [2, 2], rem = 3. Next candidate: 3.
6✅ RECORD / GOAL(Choose 3, d=3, rem=0)
path = [2, 2, 3]
path = [2, 2, 3]. rem == 0 Target Hit →\rightarrow→ Record [2, 2, 3] ✅.
7✂️ PRUNED(Backtrack / Pop 3, d=2)
path = [2, 2]
path = [2, 2]. Next candidate 6 > 3 →\rightarrow→ ✂️ PRUNE & BREAK.
8↩️ BACKTRACK / POP(Backtrack / Pop 2, d=1)
path = [2]
path = [2], rem = 5. Next candidate: 3.
9✂️ PRUNED(Choose 3, d=2, rem=2)
path = [2, 3]
path = [2, 3]. Next candidate 3 > 2 →\rightarrow→ ✂️ PRUNE & BREAK.
10🌱 ROOT(Backtrack to Root, d=0)
Next candidate at root is 3.
11✂️ PRUNED(Choose 3, d=1, rem=4)
path = [3]
path = [3]. Reuse 3 →\rightarrow→ path = [3, 3] (rem=1). Next 3 > 1 →\rightarrow→ ✂️ PRUNE.
12🌱 ROOT(Root Candidate 6, d=1)
path = [6]
path = [6] (rem=1). Next 6 > 1 →\rightarrow→ ✂️ PRUNE.
13🌱 ROOT(Root Candidate 7, d=1)
path = [7]
path = [7] (rem=0). Target Hit →\rightarrow→ Record [7] ✅. Search Complete: Output [[2, 2, 3], [7]].
Code Shape
Archetype Code Shape
Python
1candidates.sort(), results = [], path = []
2def dfs(start, remaining):
3 if remaining == 0:
4 results.append(list(path))
5 return
6 for i in range(start, len(candidates)):
7 if candidates[i] > remaining:
8 break
9 path.append(candidates[i])
10 dfs(i, remaining - candidates[i])
11 path.pop()
12dfs(0, target)
13return results

Conceptual Narrative

🧭 Conceptual Foundation & Pattern Intuition

Every earlier archetype recursed on i + 1, permanently retiring an element once chosen. Here, elements can be reused unlimited times — and the fix is a one-character change with a big conceptual meaning.

The Analogy: An Unlimited Supply of Coin Denominations

Think of this like making change with coins, but with an unlimited supply of every denomination. After choosing to use a $2 coin, you're not forbidden from using another $2 coin next — the coin type stays available for reuse. What does stay forbidden is going back to a $1 coin after you've moved on to considering $2s and larger, since that would just generate the same combination in a different order ([1,2] and [2,1] are the same combination of coins, unlike with permutations).

🔑 The Key Idea in the Code

dfs(i, ...) instead of dfs(i + 1, ...) is the entire trick: the current candidate remains a legal choice for the very next recursive call, while candidates before i are still permanently excluded — giving reuse of the current element without ever re-allowing an earlier, already-passed-over one.

  • Advancing index i + 1 instead of i: Because elements can be reused unlimited times, the recursive call must pass i (not i + 1) to allow the same element to be chosen again.

  • Exploring candidates when current_sum + num > target: Sorting candidates upfront allows an immediate break from the loop as soon as candidates[i] > remaining, pruning entire subtrees.

  • Restarting loop at 0 instead of start: Starting the candidate loop at 0 generates permutations instead of combinations (e.g. [2, 3] and [3, 2]). Always start loop at start index.

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

"Return all combinations summing to target" + explicit unlimited reuse of the same value + distinct candidates.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

Invariant: remaining is always target minus the sum of path. Complete when remaining == 0. A candidate c is prunable the moment c > remaining, and because candidates are sorted, every later candidate is prunable too.

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

  • Passing i + 1 (forbids reuse, becomes Combination Sum II's shape) when the problem explicitly allows reuse -- must pass i.

  • Using continue instead of break on overshoot -- correct either way, but break is the O(N^(T/m))-improving optimisation sorted input licenses, and an interviewer will ask why.

  • Forgetting to sort first -- without it, the overshoot prune cannot safely break, since a later unsorted candidate might still be small enough.

The 60-Second Interview Pitch

Say this out loud before you type a single line

"I sort candidates first, then DFS with (start_index, remaining). If remaining == 0 I record a copy of path. Otherwise I loop from start_index: if candidates[i] > remaining I break, since sorting guarantees every later candidate also overshoots. Otherwise I append it, recurse with (i, remaining - candidates[i]) -- passing i, not i + 1, to allow reuse -- then pop. Time is roughly O(N^(T/m)) where m is the smallest candidate."

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(N^(T/M))

Look at the execution steps: Let T = target, M = min(candidates), N = len(candidates). Maximum tree depth is T / M. In the worst case, branching factor is N, giving O(N^(T/M)) operations.

SPACE COMPLEXITY

O(T/M) Call Stack

Recursion stack depth <= T / M; path buffer size <= T / M.

Formal Recurrence Relation

T(N, T) = O(N^{T / min(C)})

Derivation Progression

Tree Depth Bound

Max Depth = floor(target / min(candidates))

Recursion cannot descend deeper than target divided by smallest coin.

Branching Factor

N choices per state

Can reuse current element or move to next candidate index.

Over-Target Pruning

if remain < 0: return

Immediately terminates branches exceeding target sum.

Variable Definitions

NNN

Number of candidate values

TTT

Target remainder sum

MMM

Minimum candidate value in the array

Memory Architecture & Bounds

🟣 Call Stack

O(T / M) Call stack depth

🔵 Auxiliary Heap

O(T / M) Current combination path

🟢 Output Space

O(K · D) Result combinations

Boundary Best / Worst Cases

Best Case

O(1)O(1)O(1) if single candidate equals target.

Average Case

O(NT/M)O(N^{T/M})O(NT/M).

Worst Case

O(NT/M)O(N^{T/M})O(NT/M) with small candidate values.

Decision & State-Space Search Tree

Decision & State-Space Search Tree
Synthesizing vector architecture diagram...
Staff+ Engineering Perspective

Senior SWE Deconstruction & Hardware Caveats

ARCHITECTURAL SIGNALS & INTERVIEW TRIGGERS

Triggers: "candidates sum to target", "same number chosen unlimited times". Index persistence: when candidate iii is selected, recurse with index iii (not i+1i + 1i+1) to allow repeated selection of the same element while preventing duplicate combinations in different orders.

CONSTRAINTS & BOUNDS

candidates≤30,target≤40candidates \le 30, target \le 40candidates≤30,target≤40. Sorting candidates upfront enables early loop termination when candidates[i]>remainingcandidates[i] > remainingcandidates[i]>remaining.

FAANG PRODUCTION TRAPS & EDGE CASES

Target exceeds zero: prune immediately if remaining<0remaining < 0remaining<0.

© 2026 Hi👋WebEnterprise. All rights reserved.
Sitemap•llms.txt•