LeetCode 第17题:电话号码的字母组合
题目描述
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
- 2: abc
- 3: def
- 4: ghi
- 5: jkl
- 6: mno
- 7: pqrs
- 8: tuv
- 9: wxyz
难度
中等
题目链接
https://leetcode.cn/problems/letter-combinations-of-a-phone-number/
示例
示例 1:
输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
示例 2:
输入:digits = ""
输出:[]
示例 3:
输入:digits = "2"
输出:["a","b","c"]
提示
- 0 <= digits.length <= 4
- digits[i] 是范围 ['2', '9'] 的一个数字。
解题思路
方法:回溯(深度优先搜索)
这道题可以使用回溯法求解。对于每个数字,我们需要尝试它对应的每个字母,然后继续处理下一个数字。
关键点:
- 建立数字到字母的映射表
- 使用回溯法遍历所有可能的组合
- 使用StringBuilder优化字符串拼接
- 处理空字符串的特殊情况
具体步骤:
- 处理特殊情况(空字符串)
- 初始化数字到字母的映射
- 使用回溯法:
- 当前组合长度等于输入长度时,添加到结果
- 否则,遍历当前数字对应的所有字母
- 递归处理下一个数字
- 回溯,删除最后添加的字母
时间复杂度:O(4^N × N),其中N是输入的长度
空间复杂度:O(N),递归深度
代码实现
C# 实现
public class Solution {private readonly string[] phoneMap = {"", // 0"", // 1"abc", // 2"def", // 3"ghi", // 4"jkl", // 5"mno", // 6"pqrs", // 7"tuv", // 8"wxyz" // 9};public IList<string> LetterCombinations(string digits) {List<string> result = new List<string>();// 处理空字符串if (string.IsNullOrEmpty(digits)) {return result;}// 使用StringBuilder优化字符串拼接StringBuilder current = new StringBuilder();Backtrack(digits, 0, current, result);return result;}private void Backtrack(string digits, int index, StringBuilder current, List<string> result) {// 如果当前组合长度等于输入长度,添加到结果if (index == digits.Length) {result.Add(current.ToString());return;}// 获取当前数字对应的字母string letters = phoneMap[digits[index] - '0'];// 遍历当前数字对应的所有字母for (int i = 0; i < letters.Length; i++) {// 添加当前字母current.Append(letters[i]);// 递归处理下一个数字Backtrack(digits, index + 1, current, result);// 回溯,删除最后添加的字母current.Length--;}}
}
优化版本(使用队列)
public class Solution {private readonly string[] phoneMap = {"", // 0"", // 1"abc", // 2"def", // 3"ghi", // 4"jkl", // 5"mno", // 6"pqrs", // 7"tuv", // 8"wxyz" // 9};public IList<string> LetterCombinations(string digits) {List<string> result = new List<string>();// 处理空字符串if (string.IsNullOrEmpty(digits)) {return result;}// 初始化结果为空字符串result.Add("");// 逐个处理每个数字foreach (char digit in digits) {List<string> temp = new List<string>();string letters = phoneMap[digit - '0'];// 将当前数字的每个字母与之前的结果组合foreach (string s in result) {foreach (char letter in letters) {temp.Add(s + letter);}}result = temp;}return result;}
}
代码详解
回溯版本:
- 映射表定义:
- 使用字符串数组存储数字到字母的映射
- 回溯函数:
- 使用StringBuilder优化字符串操作
- 递归处理每个数字对应的字母
- 使用index跟踪处理位置
- 结果收集:
- 当处理完所有数字时添加结果
- 回溯时删除最后添加的字母
队列版本:
- 迭代处理:
- 逐个处理每个数字
- 使用临时列表存储新的组合
- 组合生成:
- 将当前数字的每个字母与之前的结果组合
- 更新结果列表
- 优化:
- 避免使用递归
- 减少内存使用
执行结果
回溯版本:
- 执行用时:132 ms
- 内存消耗:42.8 MB
队列版本:
- 执行用时:124 ms
- 内存消耗:42.5 MB
总结与反思
- 这道题的关键点:
- 理解回溯法的应用
- 字符串组合的生成
- 优化字符串操作
- 两种解法比较:
- 回溯法:思路清晰,适合扩展
- 队列法:性能更好,代码更简洁
- 优化思路:
- 使用StringBuilder提高性能
- 预分配结果集大小
- 考虑并行处理大规模输入
相关题目
- LeetCode 第22题:括号生成
- LeetCode 第39题:组合总和
- LeetCode 第46题:全排列
- LeetCode 第78题:子集