电话号码字母组合问题要求根据电话按键上数字对应的字母,输出所有数字组合对应的所有可能字符串。比如输入23,对应按键字母是abc和def,输出结果就是ad、ae、af、bd、be、bf、cd、ce、cf。这个问题最经典的解决方法就是回溯算法,不过很多开发者在实现时会出现各类错误。

常见错误分析
1. 数字到字母的映射错误
很多开发者会忽略数字0和1没有对应字母的情况,或者把数字2对应的字母写成abc之外的错误内容。如果输入包含0或1,没有做过滤处理的话,会导致结果出现空字符串或者错误字符。
2. 递归终止条件设置错误
回溯算法需要明确的终止条件,部分开发者会把终止条件设置为当前索引超过数字长度减一,或者忘记在终止时把当前组合加入结果列表,导致结果缺失或者多输出无效内容。
3. 组合拼接逻辑错误
常见的错误是每次递归时直接修改原组合字符串,没有做回溯还原操作,或者用列表拼接时没有正确弹出最后添加的字符,导致后续递归使用了已经被修改的错误组合。
4. 结果去重逻辑冗余
这个问题本身不会出现重复的字母组合,部分开发者额外添加去重逻辑,反而增加了不必要的性能开销,属于无效代码。
回溯算法实践步骤
回溯算法的核心是先构建数字到字母的映射表,然后通过递归遍历每个数字对应的所有可能字母,每次选择一个字母加入当前组合,递归到下一个数字,直到遍历完所有数字,就把当前组合加入结果集,然后回溯去掉最后加入的字母,继续尝试下一个可能。
步骤一:构建映射表
首先需要建立数字和对应字母的映射关系,这里用字典实现,注意排除0和1这两个没有对应字母的数字。
# 构建数字到字母的映射字典
phone_map = {
"2": "abc",
"3": "def",
"4": "ghi",
"5": "jkl",
"6": "mno",
"7": "pqrs",
"8": "tuv",
"9": "wxyz"
}
步骤二:定义回溯函数
回溯函数需要接收当前遍历到的数字索引、当前已经拼接的组合字符串、原始输入的数字字符串、映射表、结果列表这几个参数。当索引等于数字字符串的长度时,说明已经遍历完所有数字,把当前组合加入结果列表。
def backtrack(index, current, digits, phone_map, result):
# 终止条件:遍历完所有数字
if index == len(digits):
result.append(current)
return
# 获取当前数字对应的字母串
current_digit = digits[index]
letters = phone_map.get(current_digit, "")
# 遍历当前数字的所有可能字母
for letter in letters:
# 选择当前字母,递归处理下一个数字
backtrack(index + 1, current + letter, digits, phone_map, result)
步骤三:主函数封装
主函数先处理输入为空的情况,避免不必要的递归,然后初始化结果列表,调用回溯函数,最后返回结果。
def letter_combinations(digits):
# 输入为空时直接返回空列表
if not digits:
return []
phone_map = {
"2": "abc",
"3": "def",
"4": "ghi",
"5": "jkl",
"6": "mno",
"7": "pqrs",
"8": "tuv",
"9": "wxyz"
}
result = []
# 初始调用回溯函数,索引从0开始,当前组合为空字符串
backtrack(0, "", digits, phone_map, result)
return result
测试验证
我们可以用几个常见的输入场景测试代码是否正确:
- 输入为空字符串,返回空列表
- 输入23,返回所有abc和def的组合
- 输入包含0或1,直接跳过对应的数字,比如输入201,返回和2对应的结果一致
测试代码示例如下:
# 测试代码
print(letter_combinations("")) # 输出 []
print(letter_combinations("23")) # 输出 ['ad', 'ae', 'af', 'bd', 'be', 'bf', 'cd', 'ce', 'cf']
print(letter_combinations("201")) # 输出 ['a', 'b', 'c']
注意事项
如果输入的数字字符串长度很长,回溯的递归深度会随之增加,Python默认的递归深度是1000,如果输入长度超过这个限制,需要手动调整递归深度,或者改用迭代的方式实现。另外在实现时,尽量避免使用全局变量存储结果,把结果作为参数传递会更符合函数式编程的规范,也更容易排查错误。