17. 电话号码的字母组合
题目来源
题目分析
在这道题目中,我们需要将给定的数字字符串
digits
映射到可能的字母组合。具体来说,输入的数字字符串仅包含数字2-9
,每个数字都对应多个字母。题目要求返回所有可能的字母组合。
题目难度
- 难度:中等
题目标签
- 标签:哈希表、字符串、回溯
题目限制
- 0 <= digits.length <= 4
- digits[i] 是范围 [‘2’, ‘9’] 的一个数字。
解题思路
要解决这个问题,我们可以使用回溯算法。回溯算法能够在树形结构中寻找所有可能的解,其核心在于递归地处理问题的每一层,并在合适的时候回退以探索新的路径。
核心算法步骤
-
映射关系: 首先,我们需要一个映射来存储每个数字对应的字母集合。这可以通过一个字符串数组
NUMBER_MAP
来实现。 -
回溯函数: 我们定义一个回溯函数
backtrace
,通过递归的方式构造出每一个可能的字母组合。每次递归调用时,我们都会处理index
指向的当前数字对应的字母,并向结果集中添加可能的组合。在递归完成后,我们需要“恢复现场”,即回退上一个字符,以便探索新的可能性。 -
边界条件: 当我们遍历完所有的数字时,将当前的组合加入结果集。
代码实现
以下是实现电话号码字母组合的Java代码:
/**
* 17. 电话号码的字母组合
* @param digits 输入的数字串
* @return ans 所有能表示的字母组合
*/
public List<String> letterCombinations(String digits) {
List<String> ans = new ArrayList<>();
if (digits.isEmpty()) {
return ans;
}
backtrace(digits, 0, new StringBuilder(), ans);
return ans;
}
public void backtrace(String digits, int index, StringBuilder sb, List<String> ans) {
// 边界条件
if (index == digits.length()) {
ans.add(sb.toString());
return;
}
char c = digits.charAt(index);
for (int i = 0; i < NUMBER_MAP[c - '0'].length(); i++) {
sb.append(NUMBER_MAP[c - '0'].charAt(i));
backtrace(digits, index + 1, sb, ans);
// 恢复现场
sb.deleteCharAt(sb.length() - 1);
}
}
代码解读
-
边界条件: 首先检查输入的
digits
是否为空,如果为空,直接返回空列表。 -
回溯过程: 对于输入字符串的每个字符,根据映射关系将其对应的字母添加到当前路径中,并递归处理下一个字符。在回溯的过程中,我们不断将已经构造好的组合添加到结果集中。
-
恢复现场: 每次递归结束后,需要将当前路径的最后一个字符移除,以便尝试新的字母组合。
性能分析
-
时间复杂度:
O(4^n)
,其中n
是输入字符串的长度。因为每个数字最多对应4个字母,所有可能的组合数量为4^n
。 -
空间复杂度:
O(n)
,由于递归调用栈的深度为n
。
测试用例
你可以使用以下测试用例来验证代码的正确性:
List<String> result = letterCombinations("23");
System.out.println(result);
// 输出: ["ad","ae","af","bd","be","bf","cd","ce","cf"]
扩展讨论
优化写法
我们可以使用更加简洁的字符串操作,减少不必要的递归调用,进一步优化性能。
其他实现
除了回溯算法,还可以通过迭代的方式逐步构建字母组合,但相对来说代码复杂度会更高。
总结
通过这道题目,我们进一步掌握了回溯算法的应用,尤其是在处理树形结构问题时的优雅解法。通过递归的方式,我们可以有效地生成所有可能的字母组合,并确保不会遗漏任何一种情况。