回溯法可以解决的问题:
排列,组合问题,满足某种条件的全部子集,即需要穷举才能完成的任务就可能需要回溯。
排列有序,组合无序
回溯法模板
void back(参数) {
if (End condition) {
//Processing result;
return;
}
for (dataset) {
//Processing data(比如储存);
back(参数); // 递归
//revoke processing(如删除本次储存)
}
}
例题
组合问题
leetcode 例题:
组合问题:给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
vector<vector<int>> combine(int n, int k) {
back(1,n,k);
return res;
}
vector<vector<int>> res;
vector<int> tem;
void back( int begin, int end, int size)
{
if(tem.size() == size)
{
res.push_back(tem);
return;
}
for(int i = begin; i<= end;i++)
//for(int i = begin; i<= end - (size - tem.size()) +1;i++)//剪枝
{
tem.push_back(i);
back(i+1,end,size);
tem.pop_back();
}
}
//尝试反过来,从大到小输出,就可以在combine实现递归,但是在leetcode上提交会超时。故只能这样,但是没有发现这样写的优点在哪,按理两种思路的耗时应该相同的
对于类似组合问题的问题(如划分子集),基本就是这样一个框架。
排列问题
给定一个不含重复数字的数组 nums ,返回其所有可能的全排列 。
vector<vector<int>> res;
vector<int> vec;
vector<vector<int>> permute(vector<int>& nums) {
unordered_set<int> set;
back(nums,set);
return res;
}
void back(vector<int>& nums,unordered_set<int>& set){
if(vec.size() == nums.size()){
res.push_back(vec);
return;
}
for(int i = 0; i < nums.size();i++){
if(set.find(nums[i]) != set.end()) continue;
vec.push_back(nums[i]);
set.emplace(nums[i]);
back(nums,set);
s.erase(nums[i]);
vec.pop_back();
}
}
//改进,可以使用一个数组来代替set。
一般回溯都不需要返回值,是因为需要遍历全部;若只需要找到一个答案即可,则应该有返回值,通过对返回值判断,会减少无效的搜索。