● 216.组合总和III:
和77.组合一样的,树的结构都是2层,横向是9个分支,如下图,所以递归次数(即for循环)是9次。
那么上次写完组合数模板有印象了,不看模板自己写一遍。函数的参数边写边加,一开始只有k和n,然后递归函数那里需要加入一个和i相关的参数才能保证每个数字 最多使用一次。这里加一个参数m:backtracking(k,n,i+1);表示i已经处理,下一次只能处理[i+1,10]范围内的正整数。
class Solution {
public:
vector<vector<int>> result;
vector<int> comb;
int sum=0;//comb内元素之和
void backtracking(int k,int n,int m){
if(comb.size()>k || sum>n)return ;//forget了报错stack-overflow
if(comb.size()==k && sum==n){
result.push_back(comb);
return ;
}
for(int i=m;i<10;++i){
comb.push_back(i);//
sum+=i;
backtracking(k,n,i+1);//递归,只能选[i+1,10]内的
if(comb.size()!=0)comb.pop_back();//回溯
sum-=i;
}
}
vector<vector<int>> combinationSum3(int k, int n) {
backtracking(k,n,1);
return result;
}
};
注意:1、跟77不是完全一样,我们需要统计和,所以设置sum变量,处理节点的操作包括2步:(1)、节点入vector;(2)、节点加到sum里面。所以递归之后的回溯也是两步。
2、还有77题返回的结果是不会为空的,放int的vector容器装满了k个就能倒。但是这道题装满了k+1个,或者目前的sum比n还大了,这两种情况都需要直接返回(剪枝)。少了这个递归出口,会报错stack-overflow(第一次运行的错误)。因为没有符合条件的组合的时候这个函数会一直出不来。
学到了:在编写递归函数时,考虑递归出口(也称为基本情况或终止条件)至关重要,以确保递归能够在正确的时机停止。以下是应该在递归函数中考虑的几种关键类型的递归出口:
1、不符合条件的情况: 这些是指那些不满足您寻找解决方案所需条件的情况。例如,在这里,如果组合中数字的和已经超过了目标数n,或者组合中的数字数量超过了k,这时应立即停止进一步的递归调用(即剪枝),因为这条路径不能导致有效解。
2、明显符合条件的情况: 这些是递归搜索成功找到一个有效解的情况。在这里,当组合中的数字数量恰好为k,并且它们的和等于n时,您找到了一个有效的解决方案,应将其添加到结果集中,并停止进一步的递归调用。
正确处理这些递归出口的关键在于确保递归函数可以在所有情况下有效地终止,避免无限递归或不必要的计算。同时,这也有助于防止像栈溢出这样的运行时错误。
● 17.电话号码的字母组合
这个题是在多个集合里面找组合,之前的回溯是在一个集合里面统计组合。
为了不写很多的条件语句,需要设置一个二维数组来存放数字2-9到字母'a'-'z'的映射关系。
题目的结构应该是下面这样,一开始想错了。下面这样的结构,每一个叶子结点都是一个唯一的确定的结果。
所以,这个层高是digits.size(),每一层的横向节点数是某个数字代表的字母集合的size。所以参照之前回溯的知识点,for循环应该是横向节点数决定,所以应该是某个数字代表的字母集合的size。
然后就需要一个参数index,for循环里面递归的时候传入的得是index+1,代表着往下一层走(纵向遍历)。所以这个index就代表层次数,当index=digits.size()的时候,就代表遍历(处理)到了这颗树的叶子结点的孩子节点(即空),那么就可以回溯(往回走)了。这个过程感觉就像对这棵树先序遍历。
class Solution {
public:
string str;
vector<string> result;
const string num_char[10]={"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz",};//1
//和前面的区别:前面是在一个集合里面找组合,这个是两个集合里面找组合
void backtracking(const string & digits,int index){
if(index==digits.size()){
result.push_back(str);
return ;
}
int num=digits[index]-'0';// index ∈ [0, digits.size() - 1]
string str1=num_char[num];
for(int i=0;i<str1.size();++i){
str.push_back(str1[i]);
backtracking(digits,index+1);//下一层,
str.pop_back();
}
}
vector<string> letterCombinations(string digits) {
result.clear();
str.clear();
if(digits.size()==0){
return result;
}
backtracking(digits,0);
return result;
}
};
所以这道题主要还是要确定for循环的次数和递归传入的参数。明确这个i是"abc"(或其他)字符串的大小范围。这个index是层次数,也就是digits的size决定的。