🧠
Brute Force (Pure Recursion with Backtracking)
💡 Starting with brute force helps understand the problem structure and recursion tree, even though it is inefficient and duplicates are not handled optimally.
Intuition
Try every possible combination by recursively choosing or skipping each candidate, and check if the sum matches the target. This explores all subsets.
Algorithm
- Sort the candidates to help identify duplicates later.
- Recursively explore each candidate starting from current index.
- Include the candidate and recurse with reduced target and next index.
- Exclude the candidate and recurse with next index.
💡 The recursion explores all subsets but does not yet handle duplicates properly, so it may generate repeated combinations.
from typing import List
def combinationSum2(candidates: List[int], target: int) -> List[List[int]]:
candidates.sort()
res = []
def backtrack(start, path, target):
if target == 0:
res.append(path[:])
return
if target < 0:
return
for i in range(start, len(candidates)):
# No duplicate handling here
backtrack(i + 1, path + [candidates[i]], target - candidates[i])
backtrack(0, [], target)
return res
# Driver code
if __name__ == '__main__':
print(combinationSum2([10,1,2,7,6,1,5], 8))
Line Notes
candidates.sort()Sorting helps to identify duplicates in later approaches
def backtrack(start, path, target):Defines recursive function exploring subsets from 'start'
if target == 0:Base case: found a valid combination summing to target
for i in range(start, len(candidates))Try each candidate starting from current index
import java.util.*;
public class CombinationSum2 {
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
List<List<Integer>> res = new ArrayList<>();
backtrack(candidates, target, 0, new ArrayList<>(), res);
return res;
}
private void backtrack(int[] candidates, int target, int start, List<Integer> path, List<List<Integer>> res) {
if (target == 0) {
res.add(new ArrayList<>(path));
return;
}
if (target < 0) return;
for (int i = start; i < candidates.length; i++) {
// No duplicate handling here
path.add(candidates[i]);
backtrack(candidates, target - candidates[i], i + 1, path, res);
path.remove(path.size() - 1);
}
}
public static void main(String[] args) {
CombinationSum2 sol = new CombinationSum2();
System.out.println(sol.combinationSum2(new int[]{10,1,2,7,6,1,5}, 8));
}
}
Line Notes
Arrays.sort(candidates);Sorting to prepare for duplicate detection in better approaches
private void backtrack(...)Recursive helper exploring subsets from 'start' index
if (target == 0)Found a valid combination, add a copy to results
for (int i = start; i < candidates.length; i++)Try each candidate from current index
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
sort(candidates.begin(), candidates.end());
vector<vector<int>> res;
vector<int> path;
backtrack(candidates, target, 0, path, res);
return res;
}
void backtrack(vector<int>& candidates, int target, int start, vector<int>& path, vector<vector<int>>& res) {
if (target == 0) {
res.push_back(path);
return;
}
if (target < 0) return;
for (int i = start; i < candidates.size(); i++) {
// No duplicate handling here
path.push_back(candidates[i]);
backtrack(candidates, target - candidates[i], i + 1, path, res);
path.pop_back();
}
}
};
int main() {
Solution sol;
vector<int> candidates = {10,1,2,7,6,1,5};
vector<vector<int>> res = sol.combinationSum2(candidates, 8);
for (auto &comb : res) {
cout << "[";
for (int i = 0; i < comb.size(); i++) {
cout << comb[i];
if (i != comb.size() - 1) cout << ",";
}
cout << "]\n";
}
return 0;
}
Line Notes
sort(candidates.begin(), candidates.end());Sort to prepare for duplicate handling later
void backtrack(...)Recursive function exploring subsets from 'start'
if (target == 0)Base case: found valid combination
for (int i = start; i < candidates.size(); i++)Try each candidate starting from current index
function combinationSum2(candidates, target) {
candidates.sort((a,b) => a - b);
const res = [];
function backtrack(start, path, target) {
if (target === 0) {
res.push([...path]);
return;
}
if (target < 0) return;
for (let i = start; i < candidates.length; i++) {
// No duplicate handling here
path.push(candidates[i]);
backtrack(i + 1, path, target - candidates[i]);
path.pop();
}
}
backtrack(0, [], target);
return res;
}
// Test
console.log(combinationSum2([10,1,2,7,6,1,5], 8));
Line Notes
candidates.sort((a,b) => a - b);Sort to help identify duplicates in better approaches
function backtrack(start, path, target)Recursive helper exploring subsets from 'start'
if (target === 0)Found a valid combination, add a copy to results
for (let i = start; i < candidates.length; i++)Try each candidate starting from current index
We explore all subsets (2^n) and copying paths costs O(n) each time we find a valid combination.
💡 For n=15, 2^15=32768 subsets, which is already large and inefficient.
Interview Verdict: TLE / Inefficient for large inputs
This approach is too slow and produces duplicates, but it helps understand the problem structure.