难度中等2033
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

示例 1:
输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]示例 2:
输入:digits = ""
输出:[]示例 3:
输入:digits = "2"
输出:["a","b","c"]提示:
0 <= digits.length <= 4digits[i]是范围['2', '9']的一个数字。
思路一:回溯
这道题的解法是用回溯的方式,在循环里面套了递归调用。本来递归就不好理解了,再加上循环的递归,就更难理解了。 我们先不考虑递归,先看看下面这个问题怎么解决。 假设输入是2,只有一个字符,那么应该怎么解呢? 按照题目要求 2 = “abc",所以结果应该是 ["a","b","c"] 先不用想着怎么去写递归,只思考下怎么打印出这个结果。 这个太简单了,一个循环就搞定了:
result = List()
for(i=0;i<len("abc");i++) {
tmp = i
result.add(tmp)
}
return result上面是伪代码,一个循环就搞定了。 如果输入的是 23,应该怎么做呢?23 的结果是 ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"],我们仍然不考虑怎么去写递归,只是考虑怎么把这个结果给弄出来。代码如下:
result = List()
for(i=0;i<len("abc");i++) {
for(j=0;j<len("def");j++)
tmp = i+j
result.add(tmp)
}
return result也就是说 23 这样的长度为 2 的字符串可以用两层循环搞定。 如果输入的是 234 呢,仍然不要考虑怎么去写递归,而是想怎么把结果打印出来。
result = List()
for(i=0;i<len("abc");i+=1) {
for(j=0;j<len("def");j+=1) {
for(k=0;k<len("ghi");k+=1) {
tmp = i+j+k
result.add(tmp)
}
}
}
return result这次用了三层循环。 如果输入的是 2345,那么代码可以这么写:
result = List()
for(i=0;i<len("abc");i+=1) {
for(j=0;j<len("def");j+=1) {
for(k=0;k<len("ghi");k+=1) {
for(n=0;n<len("jkl");n+=1)
tmp = i+j+k+n
result.add(tmp)
}
}
}
return result这次是用了四层循环。现在是不是能看出一些门道了?对的。循环的嵌套层数,就是输入的字符串长度。输入的字符串长度是 1,循环只有 1 层。 输入的字符串长度是 3,循环就是 3 层。如果输入的字符串长度是 10,那么循环就是 10 层。 可是输入的字符串长度是不固定的,对应的循环的嵌套层数也是不固定的,那这种情况怎么解决呢?这时候递归就派上用场了。
对于打印"2345"这样的字符串: 第一次递归就是上图中最下面的方格,然后处理完第一个字符2之后,将输入的字符改变成"345"并调用第二个递归函数 第二次递归处理3,将字符串改变成"45"后再次递归 第三次递归处理4,将字符串改变成"5"后继续递归 第四次递归处理5,将字符串改变成""后继续递归 最后发现字符串为空了,将结果放到列表中并返回 上面是从函数调用的角度去看的,而每次调用下一层递归时,都需要将本层的一些处理结果放到一个临时变量中,再传递给下一层,从这个变量层层传递的变化看,就像一棵树一样,这个算法的时间复杂度很高,是O(3^n)这个级别的,空间复杂度是O(n)。
class Solution {
public:
vector<string> alpht = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};//字符表
vector<string> ans;//用于返回最后的结果
vector<string> letterCombinations(string digits)
{
if(digits.size() == 0)
return {}; //c++11的新特性,这里返回空的vector<string>
recursion(digits, {}, 0); //交给子函数去递归处理
return ans;
}
void recursion(const string& digits, string tmp, int index)
{
if(index == digits.size())//递归结束的条件
{
ans.push_back(tmp);//把最后的结果尾插到ans去
return;
}
// 依据digits的位数进行迭代,例如digits="23"
// 第一层迭代为"2",将遍历对应的字符串"abc"并更新字符串,依次为"a","b","c"
// 第二层迭代为"3",将遍历对应的字符串"def"并更新字符串,若上一层迭代为"a",则添加、更新为"ad", "ae"与"af".
int pos = digits[index] - '0'; //pos获取digits中的字符位置
const string& tmpAlpht = alpht[pos]; //tmpAlpht获取pos位置处的字符串,而且建议传引用,避免拷贝构造
for(int j = 0; j < tmpAlpht.size(); ++j)
{
// 进行下一层迭代,注意同一层迭代时不改变s和idx等参数值
recursion(digits, tmp + tmpAlpht[j], index + 1);
}
}
};
另一种写法:(相比上面这种,减少了tmp的反复拷贝构造!)
class Solution {
public:
void backtracking(const string& digits, int startindex)
{
if(startindex == digits.size())
{
result.push_back(tmp);
return;
}
string cur = alpha[digits[startindex] - '0']; // 需要
for(int i = 0; i < cur.size(); ++i)
{
tmp += cur[i];
backtracking(digits, startindex + 1);
tmp.pop_back();
}
}
vector<string> letterCombinations(string digits) {
if(digits.size() == 0)
return {};
backtracking(digits, 0);
return result;
}
private:
vector<string> alpha{"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
vector<string> result;
string tmp = "";
};思路二:队列
我们可以利用队列的先进先出特点,再配合循环完成题目要求。 我们先将2对应的字符"a","b","c"依次放入队列中

之后再从队列中拿出第一个元素"a",跟3对应的字符"d","e","f"挨个拼接
于是队列就变成了下面这个样子:

按照同样的方式,再将"b"从队列中拿出,再跟3对应的字符"d","e","f"挨个拼接,队列又变成下面这个样子:

最后队列中就剩下我们要的结果了:

class Solution {
public:
vector<string> alpht = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
vector<string> ans;//用于返回最后的结果
vector<string> letterCombinations(string digits)
{
if(digits.size() == 0)
return {}; //c++11的新特性,这里返回空的vector<string>
queue<string> qe;
//先将第一个数字对应的字符串放到qe中去
string tmp = alpht[digits[0] - '0'];
string tmpStr;
for(int i = 0; i < tmp.size(); ++i)
{
tmpStr = tmp[i]; //要用一个string类型接收,才不会是将char类型的插入
qe.push(tmpStr);
}
//将digits遍历完
for(int i = 1; i < digits.size(); ++i)
{
//先获取下个数字对应的字符串
string alphtTmp = alpht[digits[i] - '0'];
string Front = qe.front(); // 队列首位
while(Front.size() < i + 1) // 队列首位的字符串是否小于遍历digits的子串的长度
{
for(int j = 0; j < alphtTmp.size(); ++j)
{
qe.push(Front + alphtTmp[j]);
}
qe.pop(); // 删除首位
Front = qe.front(); // 更新队列首位
}
}
while(!qe.empty()) //队列中剩下的就是结果,把他们都尾插到ans后面
{
ans.push_back(qe.front());
qe.pop();
}
return ans;
}
};