回溯法抽象为树形结构后,其遍历过程就是:for循环横向遍历,递归纵向遍历,回溯不断调整结果集。
题目:216.组合总数|||
会了77.组合,这道题是轻而易举了。
class Solution {
private:
vector<vector<int>> result;
vector<int> path;
void backtracking(int n, int k, int startIndex){
if(path.size() == k){
if(n == 0){
result.push_back(path);
return;
}
}
for(int i = startIndex; i <= 9; i++){
n -= i;
path.push_back(i);
backtracking(n, k, i + 1);
n += i;
path.pop_back();
}
}
public:
vector<vector<int>> combinationSum3(int k, int n) {
backtracking(n, k, 1);
return result;
}
};
题目:17.电话号码的字母组合
尝试解答:
class Solution {
private:
string path;
vector<string> result;
void backtracking(string digits, int startIndex){
if(string.size() == digits.size()){
result.push_back(path);
}
for(int i = startIndex; i < digits.size(); i++){
string.push()
}
}
public:
vector<string> letterCombinations(string digits) {
backtracking(digits, 0)
}
};
代码打到这里,就意识到本题与之前组合那道题的不同点在哪里了。即在递归的过程中for循环遍历的集合是不一样的,最高层遍历的集合为digits,而往下一层for循环遍历的集合时每一个digits中的元素对应的字母,例如:1->abc。
这应该怎么解决呢?
解决方法:映射
可以用map来做映射,也可以用二维数组来做映射
本体思路:
1.深度:输入字符串的长度
2.宽度:每一个数组所映射的字母的个数
3.参数:index:用于标记遍历到了第几个元素(不需要用startIndex做去重的操作)
4.终止条件:index == digits.size().index索引到digits最后一个元素的下一个元素。
例如输入用例"23",两个数字,那么根节点往下递归两层就可以了,叶子节点就是要收集的结果集。
那么终止条件就是如果index 等于 输入的数字个数(digits.size)了(本来index就是用来遍历digits的)。
然后收集结果,结束本层递归。
5.可进行回溯隐藏:将回溯过程隐藏在参数里
自己写代码时会有的困难:
1.怎样想到用二维数组做映射?
2.二维数组应该如何定义?
3.怎样将index所指向的数字字符映射到字母字符串,进而将该字符串作为本层for循环的范围?
4.需要加强对index这个参数的理解:index标记遍历到了digits的哪一个元素。
5.为什么如果不在主函数中对digits.size() == 0的情况单独讨论,答案就会错?
class Solution {
private:
const string letterMap[10] = {
"", //0
"", //1
"abc", //2
"def", //3
"ghi", //4
"jkl", //5
"mno", //6
"pqrs", //7
"tuv", //8
"wxyz",//9
};
public:
vector<string> result;
string path;
void backtracking(string digits, int index){
if(index == digits.size()){
result.push_back(path);
return;
}
int digit = digits[index] - '0'; //将index指向的数字转化为int
string letter = letterMap[digit]; //成功将数字字符映射到它对应的字母字符串
for(int i = 0; i < letter.size(); i++){
path.push_back(letter[i]);
backtracking(digits, index + 1);
path.pop_back();
}
}
vector<string> letterCombinations(string digits) {
path.clear();
result.clear();
if(digits.size() == 0){
return result;
} //为什么这个if判断不能省略?
backtracking(digits, 0);
return result;
}
};
本题每一个数字代表的是不同集合,也就是求不同集合之间的组合,而77. 组合 (opens new window)和216.组合总和III (opens new window)都是求同一个集合中的组合。本题是多个集合求组合,所以在回溯的搜索过程中,都有一些细节需要注意的。