Combination Sum II medium
Problem Statement
Given a collection of candidate numbers (candidates
) and a target number (target
), find all unique combinations in candidates
where the candidate numbers sum to target
.
Each number in candidates
may only be used once in the combination.
Note:
- The solution set must not contain duplicate combinations.
Example 1:
Input: candidates = [10,1,2,7,6,1,5], target = 8
Output: [[1,1,6],[1,2,5],[1,7],[2,6]]
Explanation:
1 + 1 + 6 = 8
1 + 2 + 5 = 8
1 + 7 = 8
2 + 6 = 8
Example 2:
Input: candidates = [2,5,2,1,2], target = 5
Output: [[1,2,2],[5]]
Explanation:
1 + 2 + 2 = 5
5 = 5
Steps
-
Sort the input array: Sorting allows us to easily skip duplicate numbers and prevent generating duplicate combinations.
-
Backtracking: Use a recursive backtracking function to explore all possible combinations.
-
Base Cases:
- If the current sum equals the target, add the current combination to the result.
- If the current sum exceeds the target, backtrack (return).
-
Recursive Step:
- For each candidate number:
- If the candidate is already used in the current combination (this avoids duplicates from the original array), skip it.
- Add the candidate to the current combination.
- Recursively call the function with the updated sum and combination.
- Remove the candidate from the current combination (backtracking step) to explore other possibilities.
- For each candidate number:
Explanation
The core idea is to use backtracking to explore all possible combinations. Sorting ensures we avoid duplicates efficiently. The used
array (though not strictly necessary with the sorted array and skipping of duplicates explained above, it provides a more transparent understanding of the backtracking algorithm and can improve efficiency in unsorted scenarios) tracks whether a candidate has been used in the current combination. The recursive calls systematically explore all possibilities, adding and removing candidates to build up combinations that sum to the target. We only add a combination to the results if it sums to the target.
Code
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
vector<vector<int>> result;
vector<int> combination;
vector<bool> used(candidates.size(), false); //Optional: Tracks usage to handle duplicates in unsorted input.
sort(candidates.begin(), candidates.end()); //Sort for efficiency
backtrack(candidates, target, 0, combination, result, used);
return result;
}
private:
void backtrack(vector<int>& candidates, int target, int index, vector<int>& combination, vector<vector<int>>& result, vector<bool>& used) {
if (target == 0) {
result.push_back(combination);
return;
}
if (target < 0 || index >= candidates.size()) {
return;
}
//Skip duplicate numbers
if (index > 0 && candidates[index] == candidates[index - 1] && !used[index-1]) return; //only skip if previous is not used
//Include current number
combination.push_back(candidates[index]);
used[index] = true; // Mark as used
backtrack(candidates, target - candidates[index], index + 1, combination, result, used);
//Exclude current number
combination.pop_back();
used[index] = false; //Unmark as used
backtrack(candidates, target, index + 1, combination, result, used);
}
};
int main() {
Solution solution;
vector<int> candidates = {10, 1, 2, 7, 6, 1, 5};
int target = 8;
vector<vector<int>> result = solution.combinationSum2(candidates, target);
for (const auto& combination : result) {
for (int num : combination) {
cout << num << " ";
}
cout << endl;
}
return 0;
}
Complexity
- Time Complexity: O(2N * K), where N is the number of candidates and K is the average length of a combination. In the worst case, we explore all possible subsets.
- Space Complexity: O(N * K) in the worst case, due to the recursive call stack and storing the resulting combinations. The space used by the
used
vector is O(N) which is less significant than the other factors.