????????回溯的本质是穷举,穷举所有可能,然后选出我们想要的答案,如果想让回溯法高效一些,可以加一些剪枝的操作。
????????回溯法,一般可以解决如下几种问题:
? ? ? ? 1、组合问题:N个数里面按一定规则找出k个数的集合
? ? ? ? 2、切割问题:一个字符串按一定规则有几种切割方式
? ? ? ? 3、子集问题:一个N个数的集合里有多少符合条件的子集
? ? ? ? 4、排列问题:N个数按一定规则全排列,有几种排列方式
? ? ? ? 5、棋盘问题:N皇后,解数独等等
回溯法模版:
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。
示例: 输入: n = 4, k = 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ]
? ? ? ? 根据模版,得出代码:
class Solution {
public:
vector<vector<int>> res;
vector<int> path;
void backtravel(int n,int k,int start){
if(path.size()==k){
res.push_back(path);
return;
}
for(int i=start;i<=n;++i){
path.push_back(i);//处理节点
backtravel(n,k,i+1);//递归
path.pop_back();//回溯
}
}
vector<vector<int>> combine(int n, int k) {
backtravel(n,k,1);
return res;
}
};
????????配合剪枝操作进行优化:如果for循环选择的起始位置之后的元素个数 已经不足 我们需要的元素个数了,那么就没有必要搜索了。所以起始位值至多从n-(k-path.size())+1开始。
class Solution {
public:
vector<vector<int>> res;
vector<int> path;
void backtravel(int n,int k,int start){
if(path.size()==k){
res.push_back(path);
return;
}
for(int i=start;i<=n-(k-path.size())+1;++i){
path.push_back(i);//处理节点
backtravel(n,k,i+1);//递归
path.pop_back();//回溯
}
}
vector<vector<int>> combine(int n, int k) {
backtravel(n,k,1);
return res;
}
};