class Solution:
def combinationSum3(self, k: int, n: int) -> list[list[int]]:
res = []
def backtrack(start, path, total):
if len(path) == k and total == n:
res.append(path[:])
return
if len(path) > k or total > n:
return
for i in range(start, 10):
path.append(i)
backtrack(i + 1, path, total + i)
path.pop()
backtrack(1, [], 0)
return res
class Solution {
public:
vector<vector<int>> combinationSum3(int k, int n) {
vector<vector<int>> res;
vector<int> path;
backtrack(1, k, n, path, res);
return res;
}
void backtrack(int start, int k, int n, vector<int>& path, vector<vector<int>>& res) {
if (path.size() == k && n == 0) {
res.push_back(path);
return;
}
if (path.size() > k || n < 0) return;
for (int i = start; i <= 9; ++i) {
path.push_back(i);
backtrack(i + 1, k, n - i, path, res);
path.pop_back();
}
}
};
class Solution {
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> path, List<List<Integer>> res) {
if (path.size() == k && n == 0) {
res.add(new ArrayList<>(path));
return;
}
if (path.size() > k || n < 0) return;
for (int i = start; i <= 9; i++) {
path.add(i);
backtrack(i + 1, k, n - i, path, res);
path.remove(path.size() - 1);
}
}
}
var combinationSum3 = function(k, n) {
const res = [];
function backtrack(start, path, total) {
if (path.length === k && total === n) {
res.push([...path]);
return;
}
if (path.length > k || total > n) return;
for (let i = start; i <= 9; i++) {
path.push(i);
backtrack(i + 1, path, total + i);
path.pop();
}
}
backtrack(1, [], 0);
return res;
};
The Combination Sum III problem asks you to find all unique combinations of k
numbers that add up to a given number n
, using only numbers from 1 to 9. Each number can be used at most once in each combination, and the solution set must not contain duplicate combinations.
k
numbers.
For example, if k = 3
and n = 7
, the answer is [[1,2,4]]
since 1 + 2 + 4 = 7 and all numbers are unique and within 1-9.
When approaching this problem, the first idea is often to try all possible combinations of numbers from 1 to 9 and check which ones sum to n
and have exactly k
elements. However, this brute-force approach is inefficient because the number of combinations grows rapidly as k
increases.
To optimize, we can use backtracking—a systematic way to build up combinations and abandon them early if they can't possibly lead to a solution. Backtracking is like exploring a decision tree: at each node, we decide whether to include a number, and we stop exploring a path as soon as it becomes invalid (for example, if the sum exceeds n
or the combination gets too long).
This approach avoids unnecessary work and ensures that each combination is built in a way that meets the constraints, without duplicates or repeated numbers.
We'll use a backtracking algorithm to efficiently generate all valid combinations:
k
and the sum is n
, add it to the results.k
or the sum exceeds n
, stop exploring this path (prune the branch).This method ensures we only explore valid paths and efficiently find all unique combinations.
Let's walk through the process for k = 3
and n = 9
:
[]
and total 0
.
1
: [1]
, total 1
.
2
: [1,2]
, total 3
.3
: [1,2,3]
, total 6
. Not enough, try next number.4
: [1,2,4]
, total 7
. Still not enough, try next.5
: [1,2,5]
, total 8
.6
: [1,2,6]
, total 9
. Now length is 3 and total is 9, so [1,2,6]
is a valid combination.[1,3,5]
= 9, so add that.[[1,2,6], [1,3,5], [2,3,4]]
.
Each step only explores valid paths, efficiently finding all solutions.
Brute-force approach: Would try all subsets of 1-9, which is 2^9 = 512
possible combinations. For each, check if length is k
and sum is n
. This is inefficient as k
grows.
Optimized backtracking approach:
n
or the length exceeds k
.
k
numbers from 9), since each valid combination is generated once.
The Combination Sum III problem is an excellent example of how backtracking can be used to efficiently generate all valid combinations under strict constraints. By pruning invalid paths early and ensuring each number is used at most once, we avoid unnecessary work and guarantee uniqueness of combinations. The solution is elegant because it leverages the structure of the problem to minimize computation, making it both efficient and easy to understand.