模版
pathV可反复使用,他相当于整个遍历中的一个容器,我们前序遍历向容器中加入元素,回溯的时候后序遍历,从容器中去掉元素
void backtrack(vector<int> &nums,vector<int> &pathV)
{
//1. 结束条件:并加入结果集
if (nums.size() == pathV.size())
{
vvRes.push_back(pathV);
return;
}
//2. 选择列表中的所有值进行遍历,也就是子树中能够选择的值
for (int i = 0; i < nums.size(); i++)
{
// 3. 判断合法如果该路径不合法则不对该路径进行递归了
if (!isValid(pathV, nums, i)) continue;
//4. 前序遍历,添加值,该处使用的前序遍历,会先走到最左子节点
pathV.push_back(nums[i]);
backtrack(pathV, nums);
//5. 后序遍历,按左右根的顺序,向上返回时,去掉当前值
//回溯:popback让他退回到父节点的状态,因为pathV还要再次使用
pathV.pop_back();
}
}
例题
- 全排列
class Solution
{
public:
//回溯的本质是DFS
vector<vector<int>> vvRes;
vector<vector<int>> permute(vector<int> &nums)
{
vector<int> pathV;
vector<bool> used(nums.size(), false);
backtrack(nums,pathV, used);
return vvRes;
}
//pathV为每次父节点传给子节点
void backtrack(vector<int> &nums, vector<int> &pathV, vector<bool> &used)
{
//结束条件:路径的长度等于数组长度,填满了就结束
if (nums.size() == pathV.size())
{
vvRes.push_back(pathV); //把该条路径的结果添加到res中
return;
}
//选择列表中的所有值进行遍历,也就是子树中能够选择的值
for (int i = 0; i < nums.size(); i++)
{
// 如果使用过了该元素,就不向下遍历了
if(used[i] == true) continue;
used[i] = true;
//前序遍历,添加值,该处使用的前序遍历,会先走到最左子节点
pathV.push_back(nums[i]);
backtrack(nums, pathV,used);
//后序遍历,按左右根的顺序,向上返回时,去掉当前值
//回溯:popback让他退回到父节点的状态,因为pathV还要再次使用
pathV.pop_back();
used[i] = false;
}
}
};
-
- 组合总和
class Solution
{
//https://leetcode-cn.com/problems/combination-sum/solution/shou-hua-tu-jie-zu-he-zong-he-combination-sum-by-x/
public:
int targetM = 0;
vector<vector<int>> vvRes;
vector<vector<int>> combinationSum(vector<int> &candidates, int target)
{
targetM = target;
vector<int> path;
backtrack(candidates, path,0,0);
return vvRes;
}
void backtrack(vector<int> &candidates, vector<int> &path, int index,int pathSum)
{
//结束条件:当target == sum加入结果不需要遍历
if (targetM == pathSum)
{
vvRes.push_back(path);
return;
}
//结束条件2:如果当前和大于目标值就不需要向下遍历了
if (pathSum > targetM) return;
//选择列表:注意横向,也就是同级别节点下一个数不会选择当前数
for (int i = index; i < candidates.size(); i++)
{
//for循环里不需要剪枝,因为选择列表无需改变
path.push_back(candidates[i]);
//这里i不需要+1,也就是纵向,向下遍历的时候还可以选当前数
backtrack(candidates, path, i, pathSum + candidates[i]);
path.pop_back();
}
}
};
-
- 子集
class Solution {
public:
vector<vector<int>> res;
vector<vector<int>> subsets(vector<int>& nums) {
vector<int> pathV;
res.push_back(pathV);
backTrack(nums,pathV,0);
return res;
}
void backTrack(vector<int>& nums, vector<int>& pathV, int start) {
//1.结束条件:不再向下遍历开始向上返回的条件
if(nums.size() == pathV.size()) {
return;
}
//2.遍历,选择列表,该题的选择列表
//注意这个选择列表,我们设置i=start表示我们横向同级别遍历2的时候不选当前树
//而下面的i+1是向子树遍历的时候不选当前数,注意区别
for(int i = start; i < nums.size(); i++) {
//3. 判断合法,是否使用过,如果使用过了,就不要了,这里都合法
//4.先序和后序
//如果没使用过
pathV.push_back(nums[i]);
res.push_back(pathV);
backTrack(nums,pathV,i+1);//这里+1表示向子树遍历的时候不能够再选当前数了
pathV.pop_back();
}
}
};