🧠
Brute Force (Pure Recursion with Backtracking)
💡 Starting with brute force helps understand the problem's search space and how to explore all combinations systematically, even if inefficient.
Intuition
Try every number from 1 to 9, recursively build combinations, and check if the combination size and sum match the requirements.
Algorithm
- Start from number 1 and try to include it in the current combination.
- Recursively try next numbers, adding them to the combination if they don't exceed size k or sum n.
- If combination size equals k and sum equals n, add it to results.
- Backtrack by removing the last number and try the next candidate.
💡 The recursion tree can be large and confusing, but it systematically tries all possibilities by building combinations step-by-step.
from typing import List
def combinationSum3(k: int, n: int) -> List[List[int]]:
res = []
def backtrack(start: int, comb: List[int], total: int):
if len(comb) == k and total == n:
res.append(comb[:])
return
if len(comb) > k or total > n:
return
for i in range(start, 10):
comb.append(i)
backtrack(i + 1, comb, total + i)
comb.pop()
backtrack(1, [], 0)
return res
# Driver code
if __name__ == '__main__':
print(combinationSum3(3, 7)) # Expected: [[1,2,4]]
print(combinationSum3(3, 9)) # Expected: [[1,2,6],[1,3,5],[2,3,4]]
Line Notes
def combinationSum3(k: int, n: int) -> List[List[int]]:Defines the function signature with input types and output type for clarity.
def backtrack(start: int, comb: List[int], total: int):Helper function to explore combinations starting from 'start' number.
if len(comb) == k and total == n:Base case: if combination size and sum match, add a copy to results.
for i in range(start, 10):Iterate from current start to 9 to avoid duplicates and maintain ascending order.
import java.util.*;
public class CombinationSum3 {
public List<List<Integer>> combinationSum3(int k, int n) {
List<List<Integer>> res = new ArrayList<>();
backtrack(1, k, n, new ArrayList<>(), res);
return res;
}
private void backtrack(int start, int k, int n, List<Integer> comb, List<List<Integer>> res) {
if (comb.size() == k && n == 0) {
res.add(new ArrayList<>(comb));
return;
}
if (comb.size() > k || n < 0) return;
for (int i = start; i <= 9; i++) {
comb.add(i);
backtrack(i + 1, k, n - i, comb, res);
comb.remove(comb.size() - 1);
}
}
public static void main(String[] args) {
CombinationSum3 solver = new CombinationSum3();
System.out.println(solver.combinationSum3(3, 7)); // [[1, 2, 4]]
System.out.println(solver.combinationSum3(3, 9)); // [[1, 2, 6], [1, 3, 5], [2, 3, 4]]
}
}
Line Notes
public List<List<Integer>> combinationSum3(int k, int n) {Main function to initiate backtracking and return results.
if (comb.size() == k && n == 0) {Base case: combination size reached and sum matched exactly.
for (int i = start; i <= 9; i++) {Loop through candidates from current start to 9 to avoid duplicates.
comb.remove(comb.size() - 1);Backtrack by removing last added number to try next candidate.
#include <iostream>
#include <vector>
using namespace std;
class Solution {
public:
vector<vector<int>> combinationSum3(int k, int n) {
vector<vector<int>> res;
vector<int> comb;
backtrack(1, k, n, comb, res);
return res;
}
private:
void backtrack(int start, int k, int n, vector<int>& comb, vector<vector<int>>& res) {
if (comb.size() == k && n == 0) {
res.push_back(comb);
return;
}
if (comb.size() > k || n < 0) return;
for (int i = start; i <= 9; i++) {
comb.push_back(i);
backtrack(i + 1, k, n - i, comb, res);
comb.pop_back();
}
}
};
int main() {
Solution sol;
auto res1 = sol.combinationSum3(3, 7);
for (auto &v : res1) {
cout << "[";
for (int num : v) cout << num << ",";
cout << "]\n";
}
auto res2 = sol.combinationSum3(3, 9);
for (auto &v : res2) {
cout << "[";
for (int num : v) cout << num << ",";
cout << "]\n";
}
return 0;
}
Line Notes
vector<vector<int>> combinationSum3(int k, int n) {Public method to start backtracking and collect results.
if (comb.size() == k && n == 0) {Check if current combination meets size and sum requirements.
for (int i = start; i <= 9; i++) {Iterate candidates from start to 9 to avoid duplicates and maintain order.
comb.pop_back();Backtrack by removing last element to explore other combinations.
function combinationSum3(k, n) {
const res = [];
function backtrack(start, comb, total) {
if (comb.length === k && total === n) {
res.push([...comb]);
return;
}
if (comb.length > k || total > n) return;
for (let i = start; i <= 9; i++) {
comb.push(i);
backtrack(i + 1, comb, total + i);
comb.pop();
}
}
backtrack(1, [], 0);
return res;
}
// Test cases
console.log(combinationSum3(3, 7)); // [[1,2,4]]
console.log(combinationSum3(3, 9)); // [[1,2,6],[1,3,5],[2,3,4]]
Line Notes
function combinationSum3(k, n) {Defines main function to find combinations.
if (comb.length === k && total === n) {Base case: combination size and sum match target.
for (let i = start; i <= 9; i++) {Loop through candidates from current start to 9 to avoid duplicates.
comb.pop();Backtrack by removing last added number to try next possibility.
TimeO(2^9) because each number 1-9 can be either included or excluded
SpaceO(k) for recursion stack and current combination storage
We explore all subsets of numbers 1 to 9, which is 2^9 = 512 subsets, pruning when size or sum constraints fail.
💡 For n=9, this means at most 512 recursive calls, which is manageable but not efficient for larger input ranges.
Interview Verdict: Accepted but not optimal
This approach works for the problem constraints but is inefficient for larger input sizes; it is useful to understand the problem structure.