前言
我在刷卡哥的“代码随想录”,自己的总结笔记均会放在“算法刷题-代码随想录”该专栏下。
代码随想录此题链接
题目
1.组合问题(回溯+剪枝)
经典的组合问题,通过回溯模板写此题
全局变量
//1.最终总的结果集
ArrayList<ArrayList<Integer>> result =
new ArrayList<ArrayList<Integer>>();//返回的结果
//2.每次组合的集合
ArrayList<Integer> path = new ArrayList<Integer>();
2. 本题思路分析:
回溯三部曲:
- 参数与返回值:
- 参数:n,k,startIndex(n是组合集的总和(开始为标准和,每次递归减去当前加入的元素值,当为0时说明此时已经到达目标总和),k是每一个组合集标准大小(是n叉树的深度),startIndex是下一次循环的开始节点)
- 返回值:void
- 终止条件:
- 当前集合元素的总和超过标准总和(即n<0),直接返回,结束次轮递归。
- 当组合集大小等于标准组合集大小,说明此时当前的组合集收集元素的数量已经满足条件了,再判断此时组合集合的总和是否满足条件,如果此时总和为0(每次减去当前加入的元素值,到最后为0说明此时集合的总和满足要去),则将此集合加入到最终总的结果集合中,结束此次递归。
- 单层循环逻辑:
- 从当前的startIndex开始,到9结束循环遍历;(可以通过剪枝,不到9结束。若当组合集合需要的元素数量大于当前循环中剩余的元素时,再继续循环就算接下来的结果均满足要求,集合中总的元素数量也不会满足要求。所以当前组合集合需要的元素数量是k-list.size(),当前循环中剩余的元素为9-i,k-list.size()<=9-i+1,=》 i <= 9 - (k - list.size()))+ 1
- 开始将当前值加入组合集合
- 将i+1设置成startIndex,开始递归。
- 递归完成后回溯,把当前值从组合集合中去除。
3. 算法实现
class Solution {
List<List<Integer>> result = new ArrayList<>();
LinkedList<Integer> list = new LinkedList<Integer>();
public List<List<Integer>> combinationSum3(int k, int n) {
backtracking(n,k,1);
return result;
}
public void backtracking(int n,int k,int startIndex){
if(n < 0){
return;
}
if(list.size() == k){
if(n == 0){
LinkedList<Integer> tmp = new LinkedList(list);
result.add(tmp);
}
return;
}
for(int i = startIndex;i <= 9 - (k - list.size()) + 1;i++){
list.add(i);
backtracking(n - i,k,i + 1);
list.removeLast();
}
}
}
4. 算法坑点
- 可以对于集合的总和进行考虑,来剪枝。(当前集合元素的总和超过标准总和(即n<0),直接返回,结束次轮递归。)
if(n < 0){
return;
}
- 可以通过对循环剪枝优化算法效率
原始未优化前
for(int i = statrtIndex;i <= 9;i++)
优化后。
for(int i = startIndex;i <= 9 - (k - list.size()) + 1;i++)