电话号码的字母组合
题目描述
给定一个仅包含数字 2-9
的字符串,返回所有它能表示的 字母组合。答案可以按 任意顺序 返回。
给出的数字到字母的映射如下(与电话按键相同):
2 -> "abc"
3 -> "def"
4 -> "ghi"
5 -> "jkl"
6 -> "mno"
7 -> "pqrs"
8 -> "tuv"
9 -> "wxyz"
示例 1:
输入: digits = "23"
输出: ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]
示例 2:
输入: digits = ""
输出: []
示例 3:
输入: digits = "2"
输出: ["a", "b", "c"]
解题思路
本题可以使用 回溯法(Backtracking) 来解决。
- 定义映射关系:使用
Map
数组存储2-9
每个数字对应的字母。 - 回溯搜索:递归构造字符串,每次从当前数字的字母集中选择一个,并递归处理下一个数字。
- 终止条件:当路径长度等于输入数字长度时,将其加入
ans
。
代码实现
from typing import Listclass Solution:def letterCombinations(self, digits: str) -> List[str]:Map = ["", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"] n = len(digits)if not n:return []path = []ans = []def backtrack(n):if n == len(digits):ans.append(''.join(path.copy()))returnfor i in range(0, len(Map[int(digits[n])])):path.append(Map[int(digits[n])][i])backtrack(n+1)path.pop()backtrack(0)return ans
时空复杂度分析
时间复杂度
- 每个数字最多对应 4 个字母,如果
digits
长度为n
,则组合数最多为4^n
。 - 时间复杂度:O(4^n)
空间复杂度
- 递归调用栈 最多深度为
n
,回溯过程中path
存储当前路径,额外O(n)
空间。 - 结果列表
ans
可能存储4^n
个字符串。 - 总空间复杂度:O(n + 4^n)
回溯流程示例
以 digits = "23"
为例,回溯过程如下:
起始状态:[]选择 'a' → [a] 选择 'd' → [ad] → 终止,加入结果回溯:移除 'd' → [a]选择 'e' → [ae] → 终止,加入结果回溯:移除 'e' → [a]选择 'f' → [af] → 终止,加入结果回溯:移除 'f' → []选择 'b' → [b] 选择 'd' → [bd] → 终止,加入结果...选择 'f' → [bf] → 终止,加入结果选择 'c' → [c] 选择 'd' → [cd] → 终止,加入结果...选择 'f' → [cf] → 终止,加入结果
最终输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]
易错点解析
为什么循环起点是0
而不像之前的题目为 n
?
如果起点为n
,那么在遍历下一个字符串时,位置为0
的字符串首位将永远遍历不到。
回溯函数参数为何传递 n+1
而不是 i+1
?
- 这里
n
代表当前处理的数字位置,而不是选取的字母索引。 - 递归进入下一层时,我们希望处理的是
digits[n+1]
对应的字母,而不是当前digits[n]
的下一个字母。 - 如果传递
i+1
,则逻辑变为跳过部分字母,导致组合不完整。
为什么 path.copy()
必须在 ans.append()
中使用?
path
是一个可变列表,如果直接append(path)
,在后续递归回溯修改path
时,会影响ans
中已经存储的内容。path.copy()
生成一个新的列表,避免后续修改对ans
产生影响。