1. 回溯法与深度遍历的联系以及不同点:
- 回溯法采用试错的思想,它尝试分步的去解决一个问题。当它发现分步的答案不能得到有效的解决时就会退回上一步甚至上几步。回溯法通常用递归的方法来解决。
- 深度遍历是树的一种遍历方法,它强调的是一头扎到底然后再往回走的途中去遍历其它结点,这个过程会一直持续到初始的根结点。
2.回溯法与动态规划的区别和共同点:
共同点:
- 一个问题都能够分为很多步骤来解决;
- 每一个步骤包含了很多种选择;
不同点:
- 动态规划强调的是问题的最优解,它只需要知道最终的结果;
- 回溯可以搜索到问题成立的所有解,是一种遍历算法,时间复杂度更高;
3.回溯法例题:
-
分析:集合中有很多个数,我每次可以拿一个数,经过多次取数,最终得到的值为target。一个问题的解决可以分为多个步骤,每个步骤又包含了多种选择,完美契合了回溯法的适用条件。
-
解决:
(1)解决回溯第一步,画递归树,假设初始值(根结点)为target,往下的每一层代表一个步骤,一层中的多个结点代表多种选择。
但是会发现5分支下出现了重复的两种解法,这是由于我们在第一次已经得出了所有以2,2为起点,其它和为 3 的所有情况,那么在同层的下一次遍历中就不应该再次将2考虑进去,它的起点应该是从自身开始(题目说了可以重复使用元素)。
(2)思路整理:采用dfs的思想,从根开始遍历,初始是递归出口(结果为0或者结果小于0);如果都不满足就从当前层开始,循环遍历每一个结点。对于每一个结点,添加进结果集(栈)后再次进行dfs遍历下一层(初始位置为该节点自己!!!).
#include<iostream>
#include<vector>
using namespace std;
class test{
public:
vector<vector<int>> res;
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<int> tmp;
dfs(0, candidates, tmp, target);
return res;
}
void dfs(int begin, vector<int> candidates,vector<int>& tmp,int target){
if (target == 0) //得到结果退出当次
{
res.push_back(tmp);
return;
}
if (target < 0) //不满足条件,直接剪枝
return;
for (int i = begin; i < candidates.size(); i++) //每一层的遍历,注意下一层不能使用上一层之前已经用过的点,因此从begin开始
{
tmp.push_back(candidates[i]);
//dfs下一层
dfs(i, candidates, tmp, target - candidates[i]);
//回溯,做类型的但是相反的操作
tmp.pop_back();
}
}
};
例题2:
分析:类似于例题1,只不过此时不允许使用重复元素,那么又会是怎样的情况呢?
解决:
(1)画递归树:
(2)分析:
与例1类似,可以想象同一层结点,对于前面的值相同的,它已经取得了以当前结点为基础的目标值,那么在后续结点中就不应该考虑以相同值开始的结点。
其次就是在dfs遍历时,由于不能重复使用,因此起始点就应该是i+1,不在包括自己。
class Solution {
public:
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
vector<int> path;
vector<vector<int>> res;
sort(candidates.begin(),candidates.end());
dfs(target, candidates, path, res, 0);
return res;
}
void dfs(int target, vector<int> candidates, vector<int>& path,vector<vector<int>>& res, int begin){
if (target < 0)
return;
if (target == 0)
{
res.push_back(path);
return;
}
for (int i = begin; i < candidates.size(); i++){
//对于当前层进行去重
if (i>begin && candidates[i] == candidates[i-1])
continue;
path.push_back(candidates[i]);
dfs(target - candidates[i], candidates, path, res, i + 1); //注意题目中每个元素只允许使用一次,因此为i+1
//回溯
path.pop_back();
}
}
};
博客引用:
回溯算法 + 剪枝(Java、Python)