简单来说,这个问题就是一个组合问题,而且逻辑上也比较简单。当然刚开始是想使用循环来做的,但是这样的话应该是要有很多个循环,而且相应的也会需要对应的索引变量。然后仔细考虑了一下,发现可能使用递归更好一些,使用递归的话也就是每遇到一个新的数字,我就往下递归一层,继续构造。思路的话,也没有什么特别的,直接看代码注释更清晰一些:
public static List<String> letterCombinations(String digits) {
List<String> result = new ArrayList<>();
if(digits.length() == 0)
return result;
//这个map就是用来存储每个数字对应的字母集合
Map<Integer, List<Character>> map = new HashMap<>(8);
List<Character> temp2 = new ArrayList<>();
temp2.add('a');
temp2.add('b');
temp2.add('c');
map.put(2, temp2);
List<Character> temp3 = new ArrayList<>();
temp3.add('d');
temp3.add('e');
temp3.add('f');
map.put(3, temp3);
List<Character> temp4 = new ArrayList<>();
temp4.add('g');
temp4.add('h');
temp4.add('i');
map.put(4, temp4);
List<Character> temp5 = new ArrayList<>();
temp5.add('j');
temp5.add('k');
temp5.add('l');
map.put(5, temp5);
List<Character> temp6 = new ArrayList<>();
temp6.add('m');
temp6.add('n');
temp6.add('o');
map.put(6, temp6);
List<Character> temp7 = new ArrayList<>();
temp7.add('p');
temp7.add('q');
temp7.add('r');
temp7.add('s');
map.put(7, temp7);
List<Character> temp8 = new ArrayList<>();
temp8.add('t');
temp8.add('u');
temp8.add('v');
map.put(8, temp8);
List<Character> temp9 = new ArrayList<>();
temp9.add('w');
temp9.add('x');
temp9.add('y');
temp9.add('z');
map.put(9, temp9);
List<String> init = new ArrayList<>();
List<Character> initial = map.get(Integer.parseInt(digits.substring(0, 1)));
for (int i = 0; i < initial.size(); ++i) {
init.add(initial.get(i).toString());
}
if (digits.length() == 1) //如果只有一个数字,那就是本身对应的字母集合
return init;
helper(digits, 1, map, result, init);
return result;
}
/**
* @param digits 存储初始的号码串
* @param index 当前这一层正要加入的数字下标,每一层都要+1
* @param map 存储每个数字对应的字母集和,不会改变
* @param result 最后要返回的结果
* @param temp 当前这一层构造好的结果,将会传到下一层,相当于每一层的result
*/
private static void helper(String digits, int index, Map<Integer, List<Character>> map, List<String> result, List<String> temp) {
//取出当前层中数字所对应的字母集和
List<Character> list = map.get(Integer.parseInt(digits.substring(index, index + 1)));
if (index == digits.length() - 1) { //如果到最后一个数字了
for (int i = 0; i < list.size(); ++i) {
for (int j = 0; j < temp.size(); ++j) {
//将temp(上一层的结果)中每个元素取出来并与当前数字对应的字母集合相拼接,
//并加入到最终的结果中
StringBuffer sb = new StringBuffer();
sb.append(temp.get(j));sb.append(list.get(i));
result.add(sb.toString());
}
}
return;
} else { //还没到最后一个数字
List<String> newTemp = new ArrayList<>();
for (int j = 0; j < temp.size(); ++j) {
for (int i = 0; i < list.size(); ++i) {
//将temp(上一层的结果)中每个元素取出来并与当前数字对应的字母集合相拼接,
//并加入到新的结果中
StringBuffer sb = new StringBuffer();
sb.append(temp.get(j));sb.append(list.get(i));
newTemp.add(sb.toString());
}
}
//得到了新的结果则继续下一层递归
helper(digits, index + 1, map, result, newTemp);
}
}
2019.11.23更新,看到了上面的代码,感觉自己是个傻*,为什么能写出那么恶心的代码来,对不起,我有罪…
首先为什么map的值类型要是list呢,直接是个String有什么不好的呢?还有,…对不起,我不知道当时为什么能想的这么复杂,还是直接贴现在刚想好的代码:
public static List<String> letterCombinations(String digits) {
Map<Integer, String> map = new HashMap<>();
map.put(2, "abc");
map.put(3, "def");
map.put(4, "ghi");
map.put(5, "jkl");
map.put(6, "mno");
map.put(7, "pqrs");
map.put(8, "tuv");
map.put(9, "wxyz");
List<String> result = new ArrayList<>();
combine(result, digits, 0, "", map);
return result;
}
private static void combine(List<String> result, String digits, int index, String cur, Map<Integer, String> map) {
if (index >= digits.length()) { //遍历了digits里所有的数字,可以加入到结果集中并返回
result.add(cur);
return;
}
String current = map.get(Integer.valueOf(digits.substring(index, index+1)));
//对当前数字对应的每一个字母都要与前面已经生成的字符串进行拼接
for (int i = 0; i < current.length(); ++i) {
combine(result, digits, index+1, cur+current.substring(i,i+1), map);
}
}