77. 组合
1、代码
class Solution {
List<List<Integer>> result = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> combine(int n, int k) {
backtracking(n,k,1);
return result;
}
public void backtracking(int n,int k,int startIndex){
//终止条件
if(path.size() == k){
result.add(new ArrayList<>(path));
return;
}
//处理k层循环的逻辑
for(int i= startIndex; i<= n- (k-path.size()) +1; i++){
//这里对开头元素的范围进行了收缩//4
//这里path收集元素之后,指针可取元素的扩大进行了对等个数的调整
path.add(i);
backtracking(n,k,i+1);
path.removeLast();
}
}
}
2、分析
1、回溯函数的模板如下:
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
2、回溯函数一般设定无返回值,因为函数一般都要将整个N叉树进行遍历,最终将结果读取到全局变量里面。
3、回溯函数首先先验证是否满足终止条件,若满足,则将结果存放在全局变量里面,然后返回。若不满足,则进行后续操作,通过使用for循环遍历集合的宽度,然后在循环里面进行递归函数的操作,遍历集合的深度。
4、由于回溯都是遍历整个集合,所以剪枝操作显得尤为重要。这里首先使用
n - k + 1
对第一层循环中题目所需要的元素个数进行了限制。例如集合为 [1,2,3,4] , n = 4,k = 2。当箭头指针为 1 的时候,n-k+1=3,即指针只能在 1 到 3 的范围内遍历。当指针指向 4 的时候,path只能收集一个元素,由于题目要求收集的集合的元素个数是2,所以本次指针的跳转没有意义, 即需要将其进行剪枝操作。
5、path收集完一个元素之后,集合还需要的元素就减少了一个,即我们在 i 的循环中可以操作的元素个数又多了一个。不妨使用
n- (k-path.size()) +1
将其动态限定,限定之后使用递归遍历就可以取到题目需要的集合。
6、for循环中的回溯函数中我们需要传入的参数是题目中限定的路径,还有我们需要操作的指针。
7、由于我们是通过递归回溯来遍历集合(N叉树),所以我们在递归回溯获取到我们需要的结果之后,需要将回溯结果进行撤销,以便下一次递归回溯收集结果的进行。