Given collection of candidate numbers (candidates) and a integer target.Find all unique combinations in candidates where the sum is equal to the target.There can only be one usage of each number in the candidates combination and return the answer in sorted order.
e.g : The combination [1, 1, 2] and [1, 2, 1] are not unique.
Input : candidates = [2, 1, 2, 7, 6, 1, 5] , target = 8
Output : [ [1, 1, 6] , [1, 2, 5] , [1, 7] , [2, 6] ]
Explanation : The combinations sum up to target are
1 + 1 + 6 => 8.
1 + 2 + 5 => 8.
1 + 7 => 8.
2 + 6 => 8.
Input : candidates = [2, 5, 2, 1, 2] , target = 5
Output : [ [1, 2, 2] , [5] ]
Explanation : The combinations sum up to target are
1 + 2 + 2 => 5.
5 => 5.
Input : candidates = [2, 1, 2] , target = 5
To visualize the problem, imagine having a set of coins with different denominations and a target amount to achieve. The goal is to find all unique combinations of coins that add up to the target amount without reusing the same coin denominations multiple times in the same combination. An approach to solving this can involve systematically choosing coins, reducing the target by the coin's value, and continuing until the exact target is met or exceeded. If the target is exceeded, the combination is invalid and must be discarded.
The recursive process mirrors the real-life approach by first selecting a coin and subtracting its value from the target. It then recursively tries to find the remaining sum with the next coins. If a valid combination is found (target becomes zero), it is stored. If the target becomes negative or all coins are considered, the current path is abandoned, and the process backtracks to explore other combinations. This ensures all possible unique combinations are examined.
#include <bits/stdc++.h>
using namespace std;
class Solution {
private:
// Recursive helper function to find combinations
void func(int ind, int sum, vector<int> &nums,
vector<int> &candidates, vector<vector<int>> &ans) {
// If the sum is zero, add the current combination to the result
if(sum == 0) {
ans.push_back(nums);
return;
}
// If the sum is negative or we have exhausted the candidates, return
if(sum < 0 || ind == candidates.size()) return;
// Include the current candidate
nums.push_back(candidates[ind]);
// Recursively call with updated sum and next index
func(ind + 1, sum - candidates[ind], nums, candidates, ans);
// Backtrack by removing the last added candidate
nums.pop_back();
// Skip duplicates: if not picking the current candidate,
// ensure the next candidate is different
for(int i = ind + 1; i < candidates.size(); i++) {
if(candidates[i] != candidates[ind]) {
func(i, sum, nums, candidates, ans);
break;
}
}
}
public:
// Main function to find all unique combinations
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
vector<vector<int>> ans;
vector<int> nums;
// Sort candidates to handle duplicates
sort(candidates.begin(), candidates.end());
// Start the recursive process
func(0, target, nums, candidates, ans);
return ans;
}
};
// Main method for testing the Solution class
int main() {
Solution sol;
// Sample input
vector<int> candidates = {10, 1, 2, 7, 6, 1, 5};
int target = 8;
// Call the combinationSum2 function
vector<vector<int>> result = sol.combinationSum2(candidates, target);
// Output the result
cout << "Combinations are: " << endl;
for (const auto& combination : result) {
for (int num : combination) {
cout << num << " ";
}
cout << endl;
}
return 0;
}
import java.util.*;
class Solution {
// Recursive helper function to find combinations
private void func(int ind, int sum, List<Integer> nums,
int[] candidates, List<List<Integer>> ans) {
// If the sum is zero, add the current combination to the result
if (sum == 0) {
ans.add(new ArrayList<>(nums));
return;
}
// If the sum is negative or we have exhausted the candidates, return
if (sum < 0 || ind == candidates.length) return;
// Include the current candidate
nums.add(candidates[ind]);
// Recursively call with updated sum and next index
func(ind + 1, sum - candidates[ind], nums, candidates, ans);
// Backtrack by removing the last added candidate
nums.remove(nums.size() - 1);
// Skip duplicates: if not picking the current candidate,
// ensure the next candidate is different
for(int i = ind + 1; i < candidates.length; i++) {
if(candidates[i] != candidates[ind]) {
func(i, sum, nums, candidates, ans);
break;
}
}
}
// Main function to find all unique combinations
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> nums = new ArrayList<>();
// Sort candidates to handle duplicates
Arrays.sort(candidates);
// Start the recursive process
func(0, target, nums, candidates, ans);
return ans;
}
}
class Main {
public static void main(String[] args) {
Solution sol = new Solution();
// Sample input
int[] candidates = {10, 1, 2, 7, 6, 1, 5};
int target = 8;
// Call the combinationSum2 function
List<List<Integer>> result = sol.combinationSum2(candidates, target);
// Output the result
System.out.println("Combinations are: ");
for (List<Integer> combination : result) {
for (int num : combination) {
System.out.print(num + " ");
}
System.out.println();
}
}
}
class Solution:
def __init__(self):
self.ans = []
def func(self, ind, sum, nums, candidates):
# If the sum is zero, add the current combination to the result
if sum == 0:
self.ans.append(nums[:])
return
# If the sum is negative or we have exhausted the candidates, return
if sum < 0 or ind == len(candidates):
return
# Include the current candidate
nums.append(candidates[ind])
# Recursively call with updated sum and next index
self.func(ind + 1, sum - candidates[ind], nums, candidates)
# Backtrack by removing the last added candidate
nums.pop()
# Skip duplicates: if not picking the current candidate,
# ensure the next candidate is different
for i in range(ind + 1, len(candidates)):
if candidates[i] != candidates[ind]:
self.func(i, sum, nums, candidates)
break
def combinationSum2(self, candidates, target):
candidates.sort() # Sort candidates to handle duplicates
self.ans = []
self.func(0, target, [], candidates)
return self.ans
if __name__ == "__main__":
sol = Solution()
candidates = [10, 1, 2, 7, 6, 1, 5]
target = 8
result = sol.combinationSum2(candidates, target)
print("Combinations are: ")
for combination in result:
print(combination)
class Solution {
constructor() {
this.ans = [];
}
func(ind, sum, nums, candidates) {
// If the sum is zero, add the current combination to the result
if (sum === 0) {
this.ans.push([...nums]);
return;
}
// If the sum is negative or we have exhausted the candidates, return
if (sum < 0 || ind === candidates.length) return;
// Include the current candidate
nums.push(candidates[ind]);
// Recursively call with updated sum and next index
this.func(ind + 1, sum - candidates[ind], nums, candidates);
// Backtrack by removing the last added candidate
nums.pop();
// Skip duplicates: if not picking the current candidate,
// ensure the next candidate is different
for (let i = ind + 1; i < candidates.length; i++) {
if (candidates[i] !== candidates[ind]) {
this.func(i, sum, nums, candidates);
break;
}
}
}
combinationSum2(candidates, target) {
candidates.sort((a, b) => a - b); // Sort candidates to handle duplicates
this.ans = [];
this.func(0, target, [], candidates);
return this.ans;
}
}
// Main method for testing the Solution class
const sol = new Solution();
const candidates = [10, 1, 2, 7, 6, 1, 5];
const target = 8;
const result = sol.combinationSum2(candidates, target);
console.log("Combinations are: ");
result.forEach(combination => {
console.log(combination);
});
Time Complexity: O(2^N * N), where n is the number of coins. This accounts for exploring all subsets of coins.
Space Complexity: O(N), due to the recursion stack depth and storage for the current combination.
Q: Why sort the candidates array?
A: Sorting simplifies duplicate handling and ensures that combinations are generated in lexicographical order.
Q: How is the order of output combinations ensured?
A: Sorting candidates before recursion ensures that combinations are generated in ascending order.
Q: What if candidates can be used multiple times?
A: Modify the recursion to allow revisiting the same index instead of moving to the next index after including a number.
Q: How would you modify this to return the number of unique combinations instead of the combinations themselves?
A: Instead of storing valid combinations, maintain a counter to track the number of valid combinations found.