The problem you’re tackling is the Combination Sum problem, where you’re given a list of unique integers (candidates
) and a target
number. You need to find all possible combinations of elements from candidates
that sum up to the target
. The key points are that numbers can be reused multiple times and each combination must be unique.
Backtracking Approach:
In this approach, we explore each possible combination by recursively trying to add candidates. For each candidate, we have the option of either including it in the current combination or skipping it.
Steps:
- Base Case: When the remaining
target
becomes 0, we’ve found a valid combination, so we add it to the result list. - Recursive Case: For each candidate, we:
- Skip it if it exceeds the remaining target.
- Include it in the current combination and recursively try to find a solution with the reduced target.
- After recursion, we backtrack by removing the last element added to the combination, allowing us to try the next candidate.
Key Points:
- The combination must sum exactly to the target.
- A candidate can be reused multiple times, but the combination must be unique.
- Backtracking helps efficiently explore all possible combinations.
Code Explanation:
def combinationSum(candidates, target, current=None, start=0, result=None):
# Initialize the result list and current combination list on first call
if result is None:
result = []
if current is None:
current = []
# If the target is zero, we've found a valid combination
if target == 0:
result.append(list(current)) # Add the current combination to the result
return
# Try every candidate starting from the index `start`
for i in range(start, len(candidates)):
if candidates[i] > target: # Skip if the candidate exceeds the remaining target
continue
# Include the current candidate in the combination
current.append(candidates[i])
# Recur with the reduced target, keeping the same start index to allow repetition of elements
combinationSum(candidates, target - candidates[i], current, i, result)
# Backtrack: Remove the last element added to explore other combinations
current.pop()
return result
Explanation:
-
Initialization:
result
stores the list of all valid combinations.current
is the current combination being built.start
helps in maintaining the index to avoid duplicate combinations.
-
Base Case: If
target == 0
, it means the current combination sums up to the target, so it’s added to the result. -
Recursive Case:
- We iterate over the candidates starting from the
start
index to ensure combinations are formed by reusing candidates (since the index doesn’t change). - For each candidate, we reduce the
target
and recursively callcombinationSum
with the updated target. - After recursion, we backtrack by popping the last element in
current
to explore other possibilities.
- We iterate over the candidates starting from the
Example Usage:
candidates = [2, 3, 6, 7]
target = 7
print(combinationSum(candidates, target))
Output:
[[2, 2, 3], [7]]
Explanation of Example:
- The valid combinations that sum up to 7 are:
[2, 2, 3]
(sum is 7)[7]
(sum is 7)
Time Complexity:
The time complexity of this approach can be approximated as O(2^n) where n
is the number of candidates, since in the worst case, each candidate can either be included or excluded in every recursive call.
Space Complexity:
The space complexity is O(target) due to the depth of recursion and the storage needed for each combination being explored.
This backtracking approach is an efficient and clear way to solve the Combination Sum problem while ensuring all combinations are unique and sum up to the target.