回溯法解决的问题都可以抽象为树形结构
for循环就是遍历集合区间,可以理解一个节点有多少个孩子,这个for循环就执行多少次。
从图中看出for循环可以理解是横向遍历,backtracking(递归)就是纵向遍历,这样就把这棵树全遍历完了,一般来说,搜索叶子节点就是找的其中一个结果了
回溯法模板:
void backtracking(参数)
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
77. 组合
给定两个整数 n
和 k
,返回范围 [1, n]
中所有可能的 k
个数的组合。
你可以按 任何顺序 返回答案。
示例 1:
输入:n = 4, k = 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ]
示例 2:
输入:n = 1, k = 1 输出:[[1]]
思路:用回溯法遍历所有可能,得出结果集.为了避免重复,之前取过的数,不在放在集合里
代码参考:
class Solution {
List<List<Integer>> result=new ArrayList<>();
List<Integer>path=new LinkedList<>();
public List<List<Integer>> combine(int n, int k) {
backtracking(n,k,1);
return result;
}
void backtracking(int n,int k,int startIndex){
if(path.size()==k){
result.add(new ArrayList(path));
return;
}
for(int i=startIndex;i<=n;i++){
path.add(i);
backtracking(n,k,i+1);
path.removeLast();
}
}
}
代码优化
剪枝:
如果for循环选择的起始位置之后的元素个数 已经不足 我们需要的元素个数了,那么就没有必要搜索了。
class Solution {
List<List<Integer>> result=new ArrayList<>();
List<Integer>path=new LinkedList<>();
public List<List<Integer>> combine(int n, int k) {
backtracking(n,k,1);
return result;
}
void backtracking(int n,int k,int startIndex){
if(path.size()==k){
result.add(new ArrayList(path));
return;
}
//优化
for(int i=startIndex;i<=n+1-k+path.size();i++){
path.add(i);
backtracking(n,k,i+1);
path.removeLast();
}
}
}