1. 问题描述
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
2: “abc”,
3: “def”,
4: “ghi”,
5: “jkl”,
6: “mno”,
7: “pqrs”,
8: “tuv”,
9: “wxyz”
2. 解题思路
核心思想:回溯
画出树形图(以输入digits=”23“为例):
----------------------for循环,横向遍历-------------------->
| 数字 2 对应的集合[abc]中取字母
| / | \
| 取字母a 取字母b 取字母c
| / | \
|数字3对应的集合[def]中取字母 集合[def]中取字母 集合[def]中取字母
| / | \ / | \ / | \
递归, 取d 取e 取f 取d 取e 取f 取d 取e 取f
纵向
遍历 / | \ / | \ / | \
|
| [ad] [ae] [af] [bd] [be] [bf] [cd] [ce] [cf]
|
|
v
⭐由上图可知:
①遍历的深度即输入字符串 digits 的长度
②所有叶子节点即最终的结果集
⭐需要定义那些全局变量?
①一个字符串数组 res,用来存放最终的结果集
②一个临时字符串 temp,暂存叶子节点的结果
⭐设计的回溯函数需要哪些参数?
①题目给定的 String digits
②数字到对应字母集合的映射,可定义为一个String[ ] num2String
③记录当前遍历到第几个数字的遍历num,也表示树的深度
⭐递归终止条件:
当前树的深度(即 num)等于输入字符串 digits 的长度
例如当前输入 digits 为"23",则需要从根节点向下递归两层即可。
3. 代码实现
class Solution {
// res 存放最终组合结果
List<String> res = new ArrayList<>();
public List<String> letterCombinations(String digits) {
// 判空
// 如果 digits 为 null,则 digits.length() 就会报空指针异常。所以这里先判断是否为空,再调用函数
if (digits == null || digits.length() == 0){
return res;
}
// 建立 数字 ---> 字母集合 的映射,数组下标直接对应数字 2 ~ 9
String[] num2String = {"", "", "abc", "def", "ghi", "jkl", "mno", pqrs, "tuv", "wxyz"};
// 迭代
backTracking(digits, num2String, 0);
return res;
}
// 由于每次迭代都会获取一个字符串,选择可变且更为高效的StringBuilder类
StringBuilder tmp = new StringBuilder();
// 参数 String digits:输入的字符串,例如'23'
// 参数 String[] num2String:数字 ---> 字母集合的映射
// 参数 num:树的深度,也表示输入的 digits 中包含的数字的个数,记录当前遍历到第几个数字
private void backTracking(String digits, String[] num2String, int num){
// 递归终止条件:num 等于输入的数字个数
if (num == digits.length()){
res.add(temp.toString());
return;
}
// str 记录当前遍历的数字对应的字母集合
// 例如:若 digits = '23', num = 0
// 则 str 表示输入字符串 digits 的第一个字符'2'所对应的字母集合"abc"
// digits.charAt(num)得到一个字符,该字符与字符'0'相减,实际上是是两个字符的ASCII码相减,返回就是int
String str = num2String[digits.charAt(num) - '0'];
// 遍历 str 所表示的字母集合
for (int i = 0; i < str.length(); i++) {
temp.append(str.charAt(i));
// 递归,将 num 改为num + 1,即处理下一个数字
backTracking(digits, num2String, num + 1);
// 回溯,删除末尾字符,继续尝试
temp.deleteCharAt(temp.length - 1)
}
}
}
再写一种实现:
class Solution {
// 映射:数字 ---> 号码
private String[] num2LetterSet = {"abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
// 路径
private StringBuilder path = new StringBuilder();
// 结果集
private List<String> res = new ArrayList<>();
public List<String> letterCombinations(String digits) {
if(digits == null || digits.length() == 0) {
return res;
}
backTracking(digits,0);
return res;
}
// 回溯函数
private void backTracking(String digits, int index) {
if(path.length() == digits.length()) {
res.add(path.toString());
return;
}
// 当前数字对应的字母集合
String letterSet = num2LetterSet[digits.charAt(index)-'2'];
// 将字符串形式的字母集合转为字符数组并遍历
for(char ch: letterSet.toCharArray()) {
path.append(ch);
backTracking(digits, index + 1);
path.deleteCharAt(path.length() - 1);
}
}
}