Combination Sum
Medium · Backtracking · Explanation and code example
Backtracking (Optimal)
How it works
This optimized backtracking solution avoids exploring useless paths by using sorting + early stopping.
- We sort the numbers so that once a number makes the sum exceed the target, all numbers after it will also exceed the target - we can safely stop exploring further (break / return).
- At each position, we try every number starting from index
i, allowing reuse of the same number. - We build combinations step-by-step, and whenever the running total equals the target, we record the current list.
Sorting + pruning significantly reduces unnecessary recursion.
Steps
- Sort
numsso we can stop early when the sum exceeds the target. - Define a recursive function
dfs(i, currentList, total):- If
total == target:- Add a copy of
currentListto the result. - Return.
- Add a copy of
- If
- Loop
jfromito end of list:- If
total + nums[j] > target, stop the loop (no need to check larger numbers). - Add
nums[j]tocurrentList. - Call
dfs(j, currentList, total + nums[j])(reuse allowed). - Remove last element to backtrack.
- If
- Start recursion with
dfs(0, [], 0)and return the result.
This ensures we explore only valid combinations while eliminating unnecessary work.
Complexity
- Time complexity:
O(2 ^ frac{t}{m}) - Space complexity:
O(frac{t}{m})
Where
tis the giventargetandmis the minimum value innums.
Things to watch for
Moving to Next Index After Including an Element
Since each number can be used unlimited times, after including nums[i], you should stay at index i, not move to i + 1. Moving forward prevents reusing the same element.
Generating Duplicate Combinations
Without maintaining an index, you might generate [2,3] and [3,2] as separate combinations. Always iterate from the current index forward, never backward, to ensure each combination is generated only once in sorted order.
Not Pruning When Sum Exceeds Target
Continuing recursion when total > target wastes time exploring invalid paths. With sorted input, you can break early once total + nums[j] > target since all subsequent numbers are larger.
Python code example
from typing import List
class Solution:
def combinationSum(self, nums: List[int], target: int) -> List[List[int]]:
res = []
nums.sort()
def dfs(i, cur, total):
if total == target:
res.append(cur.copy())
return
for j in range(i, len(nums)):
if total + nums[j] > target:
return
cur.append(nums[j])
dfs(j, cur, total + nums[j])
cur.pop()
dfs(0, [], 0)
return res
C++ code example
class Solution {
public:
vector<vector<int>> res;
vector<vector<int>> combinationSum(vector<int>& nums, int target) {
sort(nums.begin(), nums.end());
dfs(0, {}, 0, nums, target);
return res;
}
void dfs(int i, vector<int> cur, int total, vector<int>& nums, int target) {
if (total == target) {
res.push_back(cur);
return;
}
for (int j = i; j < nums.size(); j++) {
if (total + nums[j] > target) {
return;
}
cur.push_back(nums[j]);
dfs(j, cur, total + nums[j], nums, target);
cur.pop_back();
}
}
};
Source and license
Adapted from NeetCode’s source · MIT license. Node types are supplied by LeetCode where needed.