17. 电话号码的字母组合 - 力扣(LeetCode)
问题分析
给定一个仅包含数字 2-9 的字符串,我们需要生成所有可能的字母组合,其中每个数字映射到一组字母(类似于电话按键)。例如:
- 数字 "2" 映射到字母 "a", "b", "c"
- 数字 "3" 映射到字母 "d", "e", "f"
- 以此类推(数字 1 忽略,因为它不对应任何字母)。
这是一个组合生成问题,我们需要为输入字符串中的每个数字位置选择对应的字母,并生成所有可能的序列。例如,输入 "23",可能的组合有 "ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"。
算法思路
为了解决这个问题,我们可以使用回溯算法(backtracking)。回溯是一种通过递归探索所有可能路径的方法,并在达到终点时记录结果。思路如下:
- 映射定义:首先,定义一个数字到字母的映射表(例如,使用数组或映射表存储每个数字对应的字符串)。
- 递归回溯 :从输入字符串的第一个字符开始,对于每个数字:
- 获取该数字对应的所有字母。
- 遍历每个字母,将其添加到当前组合中。
- 递归处理下一个数字位置。
- 当处理完所有数字时,将当前组合添加到结果列表。
- 回溯:在递归返回后,移除最后添加的字母,以便尝试其他字母。
- 基准情况:当所有数字处理完毕时(当前索引等于字符串长度),保存当前组合。
- 结果返回:递归结束后,返回所有生成的组合列表。
这种方法的时间复杂度为 O(3ᴺ × 4ᴹ) ,其中 N 是映射到 3 个字母的数字个数,M 是映射到 4 个字母的数字个数(例如数字 7 和 9)。空间复杂度为 O() L是组合长度,主要用于递归栈和结果存储。
实现过程
我们将使用 Java 实现上述思路。步骤如下:
- 定义映射表 :使用一个字符串数组
mapping,索引对应数字字符(例如,索引 2 存储 "abc")。 - 主方法 :
letterCombinations方法接受输入字符串digits。如果输入为空或 null,直接返回空列表。 - 回溯方法 :定义一个递归方法
backtrack,参数包括:currentCombination:当前构建的组合(使用StringBuilder以高效修改)。index:当前处理的数字在输入字符串中的索引。digits:输入字符串。mapping:映射表。result:存储结果的列表。
- 递归逻辑 :
- 基准情况:如果
index等于digits.length(),说明所有数字处理完毕,将当前组合添加到result。 - 否则,获取当前数字对应的字母字符串。
- 遍历每个字母:
- 将字母添加到
currentCombination。 - 递归调用
backtrack处理下一个索引(index + 1)。 - 回溯:移除
currentCombination的最后一个字母,以便尝试下一个字母。
- 将字母添加到
- 基准情况:如果
- 结果返回:初始化结果列表,调用回溯方法,返回结果。
下面是完整的 Java 实现代码:
java
import java.util.ArrayList;
import java.util.List;
class Solution { // 必须使用 Solution 类名
public List<String> letterCombinations(String digits) {
// 如果输入为空,返回空列表
if (digits == null || digits.isEmpty()) {
return new ArrayList<>();
}
// 定义数字到字母的映射表:索引对应数字字符('2' 到 '9')
String[] mapping = {
"", // 索引 0
"", // 索引 1(数字 1 无映射)
"abc", // 索引 2(数字 2)
"def", // 索引 3(数字 3)
"ghi", // 索引 4(数字 4)
"jkl", // 索引 5(数字 5)
"mno", // 索引 6(数字 6)
"pqrs", // 索引 7(数字 7)
"tuv", // 索引 8(数字 8) ← 修正这里!
"wxyz" // 索引 9(数字 9)
};
// 存储结果的列表
List<String> result = new ArrayList<>();
// 使用 StringBuilder 构建组合
StringBuilder currentCombination = new StringBuilder();
// 从索引 0 开始回溯
backtrack(currentCombination, 0, digits, mapping, result);
return result;
}
private void backtrack(StringBuilder currentCombination, int index,
String digits, String[] mapping, List<String> result) {
// 基准情况:所有数字处理完毕
if (index == digits.length()) {
result.add(currentCombination.toString());
return;
}
// 获取当前数字对应的字母
char digit = digits.charAt(index);
String letters = mapping[digit - '0'];
// 遍历当前数字对应的每个字母
for (char letter : letters.toCharArray()) {
currentCombination.append(letter); // 添加字母
backtrack(currentCombination, index + 1, digits, mapping, result);
currentCombination.deleteCharAt(currentCombination.length() - 1); // 回溯
}
}
}
代码解释
- 映射表 :
mapping数组存储了数字 0-9 的字母映射,其中索引 0 和 1 为空(因为数字 1 无映射)。 - 回溯方法 :
backtrack使用递归生成组合。StringBuilder用于高效构建和修改字符串。 - 回溯关键 :在遍历字母后,调用递归前添加字母,递归后移除字母(
deleteCharAt),这确保了在尝试其他路径时组合状态正确。 - 效率:这种方法避免了重复计算,递归深度等于输入字符串长度,适合小规模输入(LeetCode 测试用例通常较小)。
您可以调用 letterCombinations 方法测试不同输入,例如:
- 输入 "23" 返回 ["ad","ae","af","bd","be","bf","cd","ce","cf"]
- 输入 "" 返回空列表。
这个实现符合问题要求,并高效地生成了所有字母组合。