- 组合总和 III
找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:
只使用数字1到9
每个数字 最多使用一次
返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。
示例 1:
输入: k = 3, n = 7
输出: [[1,2,4]]
解释:
1 + 2 + 4 = 7
没有其他符合的组合了。
示例 2:
输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]
解释:
1 + 2 + 6 = 9
1 + 3 + 5 = 9
2 + 3 + 4 = 9
没有其他符合的组合了。
示例 3:
输入: k = 4, n = 1
输出: []
解释: 不存在有效的组合。
在[1,9]范围内使用4个不同的数字,我们可以得到的最小和是1+2+3+4 = 10,因为10 > 1,没有有效的组合。
提示:
2 <= k <= 9
1 <= n <= 60
思考
回溯未剪支
回溯未剪支,但回溯开始前处理了两种特殊情况。
- 最小的k个数之和大于n
- 最大的k个数之和小于n
class Solution {
int sum=0;
public List<List<Integer>> combinationSum3(int k, int n) {
List<List<Integer>> lists=new ArrayList<>();
List<Integer> list = new ArrayList<>();
for (int i = 1; i <= k; i++) {
sum = sum + i;
list.add(i);
}
if(sum>n) return lists;
if(sum==n) {
lists.add(list);
return lists;
}
list.clear();
sum = 0;
for (int i = 9;i>9-k;i--){
sum = sum + i;
list.add(i);
}
if(sum<n) return lists;
if(sum==n){
lists.add(list);
return lists;
}
list.clear();
sum = 0;
int startIndex = 1;
backTrack(list,lists,k,n,startIndex);
return lists;
}
private void backTrack(List<Integer> list, List<List<Integer>> lists, int k, int n,int startIndex) {
if(list.size()==k){
if(sum == n){
lists.add(new ArrayList<>(list));
}
return;
}
for (int i= startIndex;i < 10;i++){
list.add(i);
sum = sum + i;
backTrack(list,lists,k,n,i+1);
list.remove(list.size()-1);
sum = sum -i;
}
}
}
回溯+ 剪支
假定 k = 3,n = 7
- 减该节点下树枝
代码实现
if(sum>n) return;
- 减该节点旁树枝
代码实现
for (int i= startIndex;i < 10;i++){
// 这个剪支是减去右方的树枝
if(sum+i>n) return;
list.add(i);
sum = sum + i;
backTrack(list,lists,k,n,i+1);
list.remove(list.size()-1);
sum = sum -i;
}
完整代码
class Solution {
int sum=0;
public List<List<Integer>> combinationSum3(int k, int n) {
List<List<Integer>> lists=new ArrayList<>();
List<Integer> list = new ArrayList<>();
// 最小的k个数之和大于n
for (int i = 1; i <= k; i++) {
sum = sum + i;
list.add(i);
}
if(sum>n) return lists;
if(sum==n) {
lists.add(list);
return lists;
}
list.clear();
sum = 0;
// 最大的k个数之和小于n
for (int i = 9;i>9-k;i--){
sum = sum + i;
list.add(i);
}
if(sum<n) return lists;
if(sum==n){
lists.add(list);
return lists;
}
list.clear();
sum = 0;
int startIndex = 1;
backTrack(list,lists,k,n,startIndex);
return lists;
}
private void backTrack(List<Integer> list, List<List<Integer>> lists, int k, int n,int startIndex) {
// 剪支操作 这个剪支是减去其下方的支
if(sum>n) return;
if(list.size()==k){
if(sum == n){
lists.add(new ArrayList<>(list));
}
return;
}
for (int i= startIndex;i < 10;i++){
// 这个剪支是减去右方的树枝
if(sum+i>n) return;
list.add(i);
sum = sum + i;
backTrack(list,lists,k,n,i+1);
list.remove(list.size()-1);
sum = sum -i;
}
}
}
回溯模板
void backtracking(参数) {
if(剪支条件) return;
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
if(剪支条件) return;
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}