Given a set of distinct positive integers candidates and a target integer target, return a list of all unique combinations of candidates where the numbers sum to target. Each candidate may be chosen an unlimited number of times. Combinations may be returned in any order.
Example:
candidates = [2,3,6,7], target = 7
Output: [[7], [2,2,3]]
This is a classic combinational generation / unbounded knapsack style problem. Think recursively:
-
For each index
i(candidatearr[i]) you have two choices:- Pick it: subtract
arr[i]fromtarget, keep indexi(because reuse allowed), and recurse. - Skip it: move to index
i+1(don’t usearr[i]anymore) and recurse.
- Pick it: subtract
Stop when you either hit target == 0 (record solution) or run out of candidates / go negative (stop path).
Two useful optimizations:
- Sort candidates ascending and prune when
arr[i] > target(early stop). - Pass arrays by const reference to avoid copying candidate arrays at each recursion call (big win).
You could try every possible multiset by exploring counts for each candidate, but this brute force enumerates a huge number of combinations and repeats many impossible partial sums. Backtracking with pruning generates only feasible partial combinations and avoids wasted work.
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<vector<int>> res;
vector<int> ds;
combination(0, candidates, target, ds, res);
return res;
}
void combination(int ind, vector<int> arr, int target, vector<int> &ds, vector<vector<int>> &res) {
if (ind == arr.size()) {
if (target == 0) {
res.push_back(ds);
}
return;
}
if (arr[ind] <= target) {
ds.push_back(arr[ind]);
combination(ind, arr, target - arr[ind], ds, res);
ds.pop_back();
}
combination(ind + 1, arr, target, ds, res);
}candidates = [2,3,6,7], target = 7 (after sorting: same)
-
Start:
ind=0,target=7,ds=[]. -
Try take
2:ds=[2],target=5, stayind=0.-
take
2:ds=[2,2],target=3, stayind=0.- take
2: target=1 → can't (2 > 1), skip to next candidate. - skip to
3: take3→ target=0 → record[2,2,3].
- take
-
backtrack...
-
-
Skip all 2s and eventually try
7and record[7].
Results: [[2,2,3], [7]].
Let k be the number of candidates and target be T.
-
Time complexity: The worst-case time is exponential. A loose upper bound is
O(k * (T/min_cand)^{depth})but more practically the number of valid combinations is bounded by the compositions of T with parts from candidates, which is exponential in T / min(candidates). A common analysis: number of possible combinations ≈O((T/minCand)^(k))in worst cases; each combination takes up to O(T / minCand) time to build/copy. So exponential time. -
Space complexity:
- Recursion depth at most
T / minCandidate(if we keep picking min value), soO(T / minCand)stack +O(n)for each output combination stored. Output size can be large (exponential), so space complexity including output isO(#solutions * average_solution_length).
- Recursion depth at most
In short: exponential time & space in worst case (as unavoidable for enumerating all solutions).
Use a mix of simple, edge, and tricky tests.
-
Example
candidates = [2,3,6,7], target = 7Expected:[[7], [2,2,3]](order may vary)
-
Single candidate
candidates = [2], target = 8Expected:[[2,2,2,2]]
-
No solution
candidates = [5,3], target = 1→[]
-
Multiple ways, reused candidates
candidates = [2,3,5], target = 8Expected (one valid set):[3,5],[2,2,2,2],[2,3,3], etc.
-
Candidates unsorted input
candidates = [7,2,3], target = 7Sorting should still give correct[7]and[2,2,3].
-
Edge cases
candidates = [], target = 7→[]target = 0→ usually[]or[[]]depending on problem spec. For LeetCodetarget >= 1typically; you can define: if target==0 return{}or{ { } }— decide consistently.
- Sort the candidates first. Sorting lets you prune: when
arr[ind] > target, you can stop exploring further indices because they are ≥arr[ind]. - Pass by const reference:
const vector<int>& arr. - Reserve vector capacity for
ds(optional):ds.reserve(target / arr[0] + 1)to reduce reallocations. - Use iterative deepening or memoization if you only need count of combinations (not list).
- If you need combinations in a canonical order, sorting candidates and choosing them in order yields lexicographically ordered sequences (by candidate order).
- Avoid duplicates: The approach above only allows non-decreasing combinations (because we either stay at
indwhen taking or move toind+1when skipping) so each unique multiset appears once; no extra dedup logic required.
- Combination Sum II: each candidate may be used only once, and input may contain duplicates. You must sort and skip duplicates by checking
if (i>start && candidates[i]==candidates[i-1]) continue;. - Count combinations only: Use DP (coin-change style) to compute counts (polynomial time).
- Bounded repetition: each candidate has limited count — adapt recursion to loop over
take_count = 0 .. max_allowedfor each index. - Return combinations with fixed length: Add a constraint on combination size and prune accordingly.
Q: Why do we not need to de-duplicate results?
A: Because we enforce a choice ordering (
takekeeps index the same,skipincrements index). This generates combinations in non-decreasing order of candidate indices, so the same multiset cannot be generated twice.
Q: Can candidates contain duplicates?
A: The classic problem assumes distinct candidates. If duplicates exist in input, sort and remove duplicates before backtracking or handle skip-duplicate logic (as in Combination Sum II).
Q: Why sort?
A: Sorting allows early pruning: if
arr[ind] > target, you can skip trying any later candidates at this level. It also provides deterministic ordering.
Q: Can we optimize with DP / memoization?
A: If you only need the count of solutions, dynamic programming is ideal. If you need actual combinations, memoization can help prune repeated subproblems but storing solution lists for subtargets can be heavy.
Q: Is recursion depth a problem?
A: Depth equals maximum number of times you can pick the smallest candidate (
target / minCandidate). For very large targets relative to smallest candidate, this can be deep. Convert to iterative or increase recursion limits if necessary.