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

输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
示例 2:
输入:digits = ""
输出:[]
示例 3:
输入:digits = "2"
输出:["a","b","c"]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
假设输入的是'23',发现嵌套两层循环可以枚举完成,输入的是'234',三层循环可以枚举完成。
结合之前做的题,不难想得出以下结论
1.使用递归里面嵌套循环实现多层循环
2.本质还是枚举,可以使用回溯法
3.回溯法解决的问题都可以抽象为树形结构
需要抽象为属性结构,就要思考n每层循环的次数与k树的深度是多少

字符串的长度=循环的层数=递归的次数=k
n=数字映射的字符串长度
数字和字母的映射
现在需要思考的首要问题是数字和字母的如何映射
可以使用map也可以使用数组,我采用的是数组
String [] map = new String[]{"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
题解
递归函数的返回值以及参数
digits代表输入值,长度等于递归的次数
index记录digits字符遍历到第几个数字了,同时也代表树的深度
path待拼接的字符串,使用stringBuffer类型
因为每一次的循环都需要重新取映射关系,并且都是从头取到尾,所以不用像之前的样子用一个变量取控制取值的区间。相比于之前的题,这道题是求不同集合之间的组合。
List<String> res = new ArrayList<> ();
String [] map = new String[]{"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
void backtracking(String digits,int index,String path)
递归的终止条件
当index = digits.length()时,说明到叶子节点了,就需要把拼接的结果放入结果集
if(index == digits.length()){
res.add(path);
return;
}
单层递归逻辑
//首先查找映射关系是每一次递归查找,所以放在循环的外面
int curNum = digits.charAt(startIndex)-'0'; //**总结点
String curStr = map[curNum];
//循环时每层的树从左到右取值
for(int i=0 ;i<curStr.length();i++){
//单层循环是需要把当前字符加入path,回溯的时候把当前字符从中删除,这里采用传参的形式实现
backtracking(digits,startIndex+1,path+curStr.charAt(i));
}
代码
class Solution {
List<String> res = new ArrayList<> ();
String [] map = new String[]{"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
public List<String> letterCombinations(String digits) {
if(digits.length()==0)return res;
backtracking(digits,0,"");
return res;
}
void backtracking(String digits,int index,String path){
if(index == digits.length()){
res.add(path);
return;
}
int curNum = digits.charAt(index)-'0';
String curStr = map[curNum];
for(int i=0 ;i<curStr.length();i++){
backtracking(digits,index+1,path+curStr.charAt(i));
}
}
}
总结
优化
阅读了别人的题解,发现可以从拼接字符的类型进行修改
path的类型从String改为StringBuffer
我之前选择使用String是因为虽然String是引用类型,但是String的不可变性,可以将其作为参数传给下一层递归,这里就隐含着回溯,可以少写回溯的代码,但是频繁的创建新数组导致效率很低,应考虑效率优先,拼接使用StringBuffer
String的不可变性:不可以在原内存区域上进行修改,final型的value[]。修改String类型变量,是新建了一个value[]数组把新的地址赋值给该变量。
public List<String> letterCombinations(String digits) {
if(digits.length()==0)return res;
backtracking(digits,0,new StringBuilder());
return res;
}
void backtracking(String digits,int index,StringBuilder path){
if(index == digits.length()){
res.add(path.toString());//类型转换
return;
}
int curNum = digits.charAt(index)-'0';
String curStr = map[curNum];
for(int i=0 ;i<curStr.length();i++){
path.append(curStr.charAt(i)); //拼接
backtracking(digits,index+1,path);
path.deleteCharAt(path.length()-1);//回溯
}
}
总结
1 char数字类型与int类型的转换
String.charAt(Index)返回是char类型,这里需要的是int类型
//int型转化成char数字型型
(char) (1+'0') //49,这里会把'0'转化成ASC码+1 = '1'
//char数字型转化成int类型
('1'-'0') //ASC码相减=int
补充:
自动类型提升
byte/short/char -> int -> long -> float->double
当byte/short/char三种类型(同类型也是)做运算时,结果至少用int接收
java在做运算时,如果操作数均在int范围内,那么一律在int的空间内运算
2 StringBuffer与String的转化
字符串频繁的拼接操作,使用StringBuffer
构造器方法是通用的转化方法
String –> StringBuffer
构造器方法:new StringBuffer(“abc”)
append方法:sb.append(“abc”);
StringBuffer –> String
构造器方法:new String(sb)
toString方法:String s = sb.toString();
3 根据下标获取删除字符串中的元素
获取
char charAt(int index):返回某索引处的字符,return value[index]
删除
仅StringBuffer
deleteCharAt(int a)只有一个参数,使用时删除索引为a的字符
浙公网安备 33010602011771号