回溯算法的理论基础
回溯常用来解决一下问题:
回溯算法并不高效,但他可以达到单纯暴力解法达不到的效果。因为当for循环的个数不能固定时,我们可以通过递归来动态实现循环的次数。
回溯算法有基本的模板
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
和二叉树的递归遍历流程一样,分为三部:
- 第一步:确定函数的参数和返回值。
- 第二步:确定中止条件。
- 第三步:设计单层遍历的逻辑。
77.组合
-
第一步:确定参数和返回值。
参数包括:n,k,当前遍历的开始位置startIndex(注意,遍历过的元素就不要再遍历了),存放当前路径的一维数组path,存放结果的二维数组res。
-
第二步:返回条件。
当path的长度=k时返回。
-
第三步:单层递归逻辑
从startIndex开始向后遍历剩下的元素,把当前的元素加入path,进入下一层递归,最后再把当前元素移除(回溯)。
整体代码如下:
class Solution {
public List<List<Integer>> combine(int n, int k) {
List<List<Integer>> res=new ArrayList<>();
List<Integer> path=new ArrayList<>();
backtracking(n,k,1,res,path);
return res;
}
public void backtracking(int n,int k,int startIndex,List<List<Integer>> res,List<Integer> path){
if(path.size()==k){
res.add(new ArrayList<>(path));//注意这里要创建一个新的List对象
return;
}
for(int i=startIndex;i<=n;i++){
path.add(i);
backtracking(n,k,i+1,res,path);
path.remove(path.size()-1);
}
}
}