文章目录
组合
题目做出来了,但踩了很多坑,且没有使用到剪枝操作。
这里,先介绍一下回溯算法的通用模板:
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
在组合问题中,主要需要主要的点:
- 在每个节点集合寻找子解的过程中,需要每次从i+1的位置开始遍历,这样会使组合总是从小到大排列,避免重复。
- 在递归函数中,注意使用 +1 而不是 ++,如果要++,则一定要记住在回溯中–。
- 剪枝操作,这里剪枝的思想是:当前数组中剩余可以取的数要比解还需要的数量少时,就不可能成为解了,此时不再继续遍历。
以下是代码部分:
public class 组合77 {
public List<List<Integer>> combine(int n, int k) {
List<List<Integer>> res = new ArrayList<>();
List<Integer> list = new ArrayList<>();
backtrcking(res, list, n, k, 0);
return res;
}
private void backtrcking(List<List<Integer>> res, List<Integer> list, int n, int k, int start){
//如果到了叶子节点
if(k == 0){
List<Integer> l = new ArrayList<>(list);
res.add(l);
//忘记return,虽然对答案没有影响
return;
}
//额外定义一个start,让组合的数组总是从小到大排,避免重复。组合的数组始终是升序数组
//for(int i = start; i<n; i++){
//剪枝操作: n - k + 1
for(int i = start; i<n-k+1; i++){
//处理节点
//踩坑:1-n 不是0-n-1
list.add(i+1);
//递归、回溯
backtrcking(res, list, n, k-1, i+1); //踩坑:1这里是i++,而不是start++ 2刚开始用的k--,i++
list.remove(list.size()-1);
}
}
}