题目描述
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
输入与输出样例
示例 1:
输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
示例 2:
输入:digits = ""
输出:[]
示例 3:
输入:digits = "2"
输出:["a","b","c"]
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/letter-combinations-of-a-phone-number
算法描述
对于这一道题目,也是求组合的问题。之前我们已经做过了一道组合的问题,具体大家可以看一下我的这一篇博客 组合(同一集合)--经典回溯问题。我们知道了使用回溯法可以解决n个for循环的问题。比如,对于样例1,我们就可以使用两层for循环来解决问题。
class Solution {
public:
sting str1 = "abc";
string str2 = 'def';
string path;
vector<string> ans;
for(int i = 0; i < str1.size(); i++){
path.push_back(str1[i]);
for(int j = 0; j < str2.size(); j++){
path.push_back(str2.[j]);
ans.push_back(path);
path.pop_back();
}
path.pop_back();
}
return ans;
}
但是这样的方法在组合数的数量变多时,需要的for循环也就越多,时间复杂度不说,写起来就受不了。所以我们引入了回溯的方法,通过使用递归来代替for循环的嵌套。但是这道题目与之前那道题目不同的是:这题是在不同的集合之间求组合,而那道题是在同一集合内求组合。因为每一个数码对应的集合是不同的
确定数码与字母的映射关系
首先表示出每个数码与对应字母之间的映射关系并不难,我们可以通过map关联容器或者使用字符串数组来实现,这里我选择使用字符串数组来实现。
const string Yin[10] = {
" ", // -- 0
" ", // -- 1
"abc",// -- 2
"def",// -- 3
"ghi",// -- 4
"jkl",// -- 5
"mno",// -- 6
"pqrs",//-- 7
"tuv",// -- 8
"wxyz"// -- 9
//下标表示对应的数码
};
确定回溯的基本框架(边界条件,单层迭代集合,递归参数)
其次就是如何回溯了,先使用回溯三部曲把基本的框架构建出来。
void backstracking(参数){
if(边界条件){
收集结果
}
加入判断进行剪枝优化
for(横向迭代对应的集合){
相应的处理
backstracking(下一层的参数); // 利用递归实现进入下一层循环的作用
回溯处理
}
}
首先确定边界条件,也就是当递归的结果字符串的size == 电话号码的位数时即可。
其次就是单层迭代的集合是什么?也就是每个数码 i 对应的字母 Yin[ i ]。
在这个过程中,我们需要知道电话号码的位数,其次我们还需要知道现在是第几位电话号码对吧,这样才能够取得每一位对应的字母,也就是每一层递归对应的横向迭代集合。
对应的代码就可以写出来啦@~@
class Solution {
public:
const string Yin[10] = {
" ", // -- 0
" ", // -- 1
"abc",// -- 2
"def",// -- 3
"ghi",// -- 4
"jkl",// -- 5
"mno",// -- 6
"pqrs",//-- 7
"tuv",// -- 8
"wxyz"// -- 9
};
string path;
vector<string> result;
void backstracking(const string& digits, int index){
if(path.size() == digits.size()){ // 判断边界条件
result.push_back(path);
return;
}
int num = digits[index] - '0';
string temp = Yin[num]; // 取得该层的横向迭代集合
for(int i = 0; i < temp.size(); i++){ // 横向迭代
path.push_back(temp[i]);
backstracking(digits, index + 1); // 进入下一层for循环
path.pop_back(); // 回溯
}
}
vector<string> letterCombinations(string digits) {
if(!digits.size()){
return result;
}
backstracking(digits, 0);
return result;
}
};
大家可以再翻到上面的for循环版本对照的看一下,可以发现二者的思路是完全一样的,不过回溯使用递归取代了for循环,具体来说,它是使用了一层递归来等效代替一层for循环。