代码随想录 | Day29 | 回溯算法:电话号码的字母组合&&组合总和
关于这个章节,大家最好是对递归函数的理解要比较到位,听着b站视频课可能呢才舒服点,可以先去搜一搜关于递归函数的讲解,理解,再开始这个章节会比较好一些
我觉得回溯就是对传入递归函数的参数加加减减,加了的减掉,减了的加上
主要学习内容:
组合题目的模板
17.电话号码的字母组合
17. 电话号码的字母组合 - 力扣(LeetCode)
解法思路:
这道题其实主要的难点是要搞清楚模板里面的for循环到底在干些什么事情
树的每个分支其实就是每一次for循环产生的,每一层递归函数的for循环可以整整产生一层节点,比如第一层递归函数的for产生的就是要分别取a,b,c,一共循环三次,取出来三个数,所以第一层有三个节点
第二层也是类似,分别选了def,才有了ad,ae,af三个结果,所以有3*3=9个结果
而第一层的abc是数字2的集合,第二层的def是数字3的集合,所以每一层递归函数都是在遍历一个不同的数的集合,我们要做的就是让递归函数知道他这一层要遍历哪个数字的集合
1.函数参数和返回值
vector<string> res;
vector<string> s{"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};//0,1没有,从2开始对应
void backtracking(string str,string digits,int index)
res存放结果集,s存放数字对应的字符串
传入的参数,str相当于path,收集我们已经知道的答案
digits是题目中给的字符串,index传递本层递归函数要遍历哪个集合
2.终止条件
一个数字只选一个字母,所以只要我们收集的和题目给的字符串一样大,就是答案了
if(str.size()==digits.size())
{
res.push_back(str);
return;
}
3.本层代码逻辑
t表示的是我们这层递归函数选的哪个数字,是由字符串里面的数字转换过来的
s[t]对应我们选的字符串(abc还是def)
遍历自然就遍历我们选的这个字符串,从0遍历到它结束,因为它的每一个字符我们都得选一遍的
s[t][i]就是对应每个字母,我们压入字符串(这里就是把23这个例子的a压入了),然后进入下一层递归去选下一个字母(去选def里面的字母去了就)
递归的index就直接+1即可,因为我们要选的就是下一个数字对应的集合
递归结束,把刚刚压入的字母弹出(把a弹出,等i等于1压入b)
int t=(int)(digits[index]-'0');
for(int i=0;i<s[t].size();i++)
{
str.push_back(s[t][i]);
backtracking(str,digits,index+1);
//回溯
str.pop_back();
}
完整代码:
class Solution {
public:
vector<string> res;
vector<string> s{"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
void backtracking(string str,string digits,int index)
{
if(str.size()==digits.size())
{
res.push_back(str);
return;
}
int t=(int)(digits[index]-'0');
for(int i=0;i<s[t].size();i++)
{
str.push_back(s[t][i]);
backtracking(str,digits,index+1);
str.pop_back();
}
}
vector<string> letterCombinations(string digits) {
if(digits.size()==0)
return res;
string str;
backtracking(str,digits,0);
return res;
}
};
注:digits如果为空传个空就是。
39.组合总和
39. 组合总和 - 力扣(LeetCode)
思路:
和之前的思路差不多组合总和III,就是之前每层遍历是从i+1开始,是为了防止出现重复的数字,现在遍历从i开始,因为本身的数可以重复取
1.函数返回值和参数
vector<vector<int>> res;
void backtracking(vector<int> path,int sum,int index,vector<int> candidates,int target)
res记录结果
path收集当前组合
sum当前集合的总和
index从哪个数开始
candidates题目给的数组
target题目给的目标值
2.终止条件
当前总和已经大于target了,没必要再递归了
如果大小等于target,收集结果
if(sum>target)
return;
if(sum==target)
{
res.push_back(path);
return;
}
3.本层逻辑
额就是可以重复取当前数字,所以下一层递归函数传参传i而不是i+1,i+1可以避免取重复的数字
不太好理解的话大家可以画个图模拟一下
for(int i=index;i<candidates.size();i++)
{
path.push_back(candidates[i]);
backtracking(path,sum+candidates[i],i,candidates,target);
path.pop_back();
}
完整代码:
class Solution {
public:
vector<vector<int>> res;
void backtracking(vector<int> path,int sum,int index,vector<int> candidates,int target)
{
if(sum>target)
return;
if(sum==target)
{
res.push_back(path);
return;
}
for(int i=index;i<candidates.size();i++)
{
path.push_back(candidates[i]);
backtracking(path,sum+candidates[i],i,candidates,target);
path.pop_back();
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<int> path;
backtracking(path,0,0,candidates,target);
return res;
}
};