回溯
今天进入新的算法模块:回溯算法。
回溯一直隐藏在递归函数中,往往是一种暴力搜索,但比普通的for循环嵌套各个有效,更好地描述。
回溯往往用森林来形象描述!!
回溯函数模版:
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
(代码随想录)
Leetcode77 组合
题目链接:组合
文章链接:代码随想录-组合
代码
void backtracking(int cur,int n, int k, int num, vector<int>& temp)// num为加上本次的数据,temp里的个数
{
temp.push_back(cur);
if(num == k)
{
res.push_back(temp);
return;
}
for(int i = cur + 1; i <= (n - k + num + 1) ;i++)
{
backtracking(i, n, k, num + 1, temp);
temp.pop_back();
}
return;
}
vector<vector<int>> combine(int n, int k) {
vector<int> temp;
for(int i = 1; i<= n-k + 1; i++)
{
backtracking(i, n ,k, 1, temp);
temp.pop_back();
}
return res;
}
本题代码与模版有些许的出入,思想是一致的。
本题的图形结构是森林,有多个树,所有我在主函数中有一个for循环,其实完全可以放在递归函数里合二为一。
本题注意有剪枝操作,就是for循环中i的范围。
Leetcode216 组合总和||
题目链接:组合总和||
文章链接:代码随想录-组合总和||
代码
void backtracking(int k, int n, int cur, int sum)
{
int num = temp.size();
if(sum == n && num == k)
{
res.push_back(temp);
cout<<sum<<endl;
return;
}
else if(sum >= n || num >= k)
return;
for(int i = cur; i <= min(9, n -sum); i++)
{
sum+=i;
temp.push_back(i);
backtracking(k, n, i + 1, sum);
sum -= i;
temp.pop_back();
}
return;
}
vector<vector<int>> combinationSum3(int k, int n) {
backtracking(k, n , 1 , 0);
return res;
}
Leetcode17 电话号码的字母组合
题目链接:电话号码的字母组合
文章链接:代码随想录-电话号码的字母组合
代码
void backtracking(string digits, int cur) //num是已经放组合里的个数。
{
if(cur == digits.size())
{
res.push_back(temp);
return;
}
int id = digits[cur] - '0';
for(int i = 0; i < s[id].size(); i++)
{
temp.push_back(s[id][i]);
backtracking(digits,cur+1);
temp.pop_back();
}
return;
}
vector<string> letterCombinations(string digits) {
s[2] = "abc",s[3] = "def",s[4] = "ghi",s[5] = "jkl",s[6] = "mno",s[7] = "pqrs",s[8] = "tuv",s[9] = "wxyz";
if(digits.size() == 0) return res;
backtracking(digits, 0);
return res;
}
本题递归函数中仅仅需要一个for循环!!
一开始错误点在于设置了两个循环,分别多赢digital中元素和具体的string元素,但这有问题。
因为每个递归函数处理的是森林中同一层的元素,(有多个树时,都处理)。因为会调用多个递归函数!!