77. 组合
回溯的过程就是通过递归来实现的,所以回溯三部曲和递归三部曲是一样的。回溯算法是纯暴力的搜索方式,做剪枝操作也改变不了其本质。
回溯法可以抽象为一个树形结构,回溯法的搜索过程就是一个树形结构的遍历过程,for循环用来横向遍历,递归的过程是纵向遍历。
class Solution {
public:
vector<vector<int>> result;
vector<int> path;
void backtracking(int n,int k,int i){
if(path.size()==k){
result.push_back(path);
return;
}
for(;i<=n;i++){
path.push_back(i);
backtracking(n,k,i+1);
path.pop_back();
}
}
vector<vector<int>> combine(int n, int k) {
result.clear();
path.clear();
backtracking(n,k,1);
return result;
}
};
大概模拟的过程就是几层for循环:
for(;;){
for(;;){
处理节点
pop();//弹出操作
}
pop();//弹出操作
}
剪枝操作:树形结构里面每一个节点都是一个for循环的过程。递归的过程就是在树形结构里沿着树枝向下搜索的过程。for循环里面遍历的i的数值就是节点的子孩子。那么既然我们剪枝剪的就是节点的子孩子,有些子孩子是没有必要进行搜索的,我们要做的优化其实就是在for循环的范围里面。
class Solution {
public:
vector<vector<int>> result;
vector<int> path;
void backtracking(int n,int k,int i){
if(path.size()==k){
result.push_back(path);
return;
}
for(;i<=n-(k-path.size())+1;i++){//遍历的时候包括起始位置,所以要加1
path.push_back(i);
backtracking(n,k,i+1);
path.pop_back();
}
}
vector<vector<int>> combine(int n, int k) {
result.clear();
path.clear();
backtracking(n,k,1);
return result;
}
};