可以,这道"电话号码的字母组合"就按你刚才喜欢的那种方式总结。
class Solution {
public:
vector<string> res; // 保存所有字母组合
vector<string> letternumber = {
"", "", "abc", "def", "ghi",
"jkl", "mno", "pqrs", "tuv", "wxyz"
};
vector<string> letterCombinations(string digits) {
string tmp; // 当前正在构造的字符串
backtracing(0, digits, tmp);
return res;
}
void backtracing(int pos, string digits, string& tmp) {
// 所有数字都已经处理完,得到一个完整组合
if (pos == digits.length()) {
res.push_back(tmp);
return;
}
// 取出当前数字
int num = digits[pos] - '0';
// 遍历当前数字对应的所有字母
for (int i = 0; i < letternumber[num].size(); i++) {
// 做选择
tmp.push_back(letternumber[num][i]);
// 进入下一层,处理下一个数字
backtracing(pos + 1, digits, tmp);
// 回溯:撤销刚才选择的字母
tmp.pop_back();
}
}
};
这题可以按照你刚才说的"回溯参数五件套"来分析。
原始数据:digits
当前路径:tmp
当前位置:pos
额外状态:不需要
结果集:res
所以回溯函数就是:
void backtracing(int pos, string digits, string& tmp)
其中最重要的三个东西:
pos
表示当前正在处理:
digits[pos]
例如:
digits = "23"
pos = 0 → 处理数字 2
pos = 1 → 处理数字 3
pos = 2 → 全部处理完成
tmp
表示当前已经选择出来的字母组合。
例如:
""
"a"
"ad"
每次:
tmp.push_back(...)
就是选择当前数字对应的一个字母。
每次:
tmp.pop_back()
就是撤销选择。
letternumber
负责建立:
数字 → 字母
的映射。
例如:
2 → abc
3 → def
7 → pqrs
9 → wxyz
所以:
int num = digits[pos] - '0';
先把字符 '2' 转成整数 2。
然后:
letternumber[num]
就可以得到:
"abc"
这题最核心的三步依然是:
tmp.push_back(letternumber[num][i]); // 做选择
backtracing(pos + 1, digits, tmp); // 进入下一层
tmp.pop_back(); // 撤销选择
也就是:
做选择 → 递归下一层 → 撤销选择
比如:
digits = "23"
递归过程:
""
处理数字2
/ | \
a b c
处理3 处理3 处理3
/ | \ ... ...
d e f
得到:
ad
ae
af
bd
be
bf
cd
ce
cf
这道题和你前面做过的题还有一个很重要的区别:
全排列是:
每一层从"所有没有使用过的数字"中选择。
所以需要:
used
子集是:
每一层从
index后面的数字中选择。
所以需要:
index
而电话号码组合是:
第
pos层只能处理digits[pos]对应的字母。
所以这里需要的是:
pos
一句话总结这道题:
每一层递归处理一个数字,从该数字对应的字母中选择一个加入
tmp,然后pos+1处理下一个数字,递归回来后撤销当前选择。
你以后看到这种"每个位置都有若干种选择,要求组合出所有结果"的题,就可以优先想到这种回溯结构。