题目
找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
说明:
- 所有数字都是正整数。
- 解集不能包含重复的组合。
示例 1:
输入: k = 3, n = 7输出: [[1,2,4]]
示例 2:
输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]
解题思路
本题就是在 [1,2,3,4,5,6,7,8,9] 这个集合中找到和为 n 的 k 个数的组合。需要注意的是,组合是不要求子集先后顺序的。
也就是说,相较于 77.组合 这道题,本题多了一个限制,本题是要找到和为 n 的 k 个数的组合,且整个集合已经是固定的了 [1, … ,9] 。
想到这一点了,做过 77.组合 这道题之后,本题是简单一些了。
本题 k 相当于树的深度,9 就是树的宽度(因为整个集合就是9个数)。
例如 k = 2,n = 4 的话,就是在集合 [1,2,3,4,5,6,7,8,9] 中求 k(个数) = 2, n(和) = 4的组合。
选取过程如图:
图中,可以看出,只有最后取到集合(1,3)和为4 符合条件。
按照 77.组合 这道题的解题套路,我们不难写出下面的代码:
class Solution {
int n, k;
List<List<Integer>> result = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
// sum 为已经收集的元素的总和,也就是path里元素的总和
int sum = 0;
public List<List<Integer>> combinationSum3(int k, int n) {
this.n = n;
this.k = k;
backtracking(1);
return result;
}
void backtracking(int startIndex) {
// 终止条件
if (path.size() == k) {
// 如果 sum == n
if (sum == n) {
// 加入到结果集中
result.add(new ArrayList<>(path));
}
return;
}
for (int i = startIndex; i <= 9; i++) {
path.add(i);
sum += i;
backtracking(i + 1);
path.removeLast();
sum -= i;
}
}
}
还要强调一下,回溯法中递归函数参数很难一次性确定下来,一般先写逻辑,需要啥参数了,填什么参数。
老规矩,还是要聊一下剪枝
剪枝
这道题目,剪枝操作其实是很容易想到了,想必大家看上面的树形图的时候已经想到了。为了方便阅读,我把上面那张图放到这里:
已选元素总和如果已经大于n(图中数值为4)了,那么往后遍历就没有意义了,直接剪掉。
那么剪枝的地方一定是在递归终止的地方剪,代码如下:
// 剪枝
if (sum > n) {
return;
}
和 77.组合 的剪枝一样,for 循环的范围也可以剪枝,i <= 9 - (k - path.size()) + 1就可以了。
for (int i = startIndex; i <= 9 - (k - path.size()) + 1; i++) { // 剪枝
答案
Java
class Solution {
// 找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
int n, k;
List<List<Integer>> result = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
// sum 为已经收集的元素的总和,也就是path里元素的总和
int sum = 0;
public List<List<Integer>> combinationSum3(int k, int n) {
this.n = n;
this.k = k;
backtracking(1);
return result;
}
void backtracking(int startIndex) {
// 剪枝
if (sum > n) {
return;
}
// 终止条件
if (path.size() == k) {
if (sum == n) {
// 加入到结果集中
result.add(new ArrayList<>(path));
}
return;
}
for (int i = startIndex; i <= 9; i++) {
path.add(i);
sum += i;
backtracking(i + 1);
path.removeLast();
sum -= i;
}
}
}
REF
https://programmercarl.com/0216.组合总和III.html
https://leetcode-cn.com/problems/combination-sum-iii/
