(LeetCode-Hot100)17. 电话号码的字母组合

电话号码的字母组合(LeetCode 17)

🔗 LeetCode 中文链接


📌 问题简介

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

复制代码
2 -> "abc"
3 -> "def"
4 -> "ghi"
5 -> "jkl"
6 -> "mno"
7 -> "pqrs"
8 -> "tuv"
9 -> "wxyz"

📥 输入输出说明

  • 输入 :字符串 digits(长度范围:0 ≤ digits.length ≤ 4,只包含 '2' 到 '9')
  • 输出:所有可能的字母组合列表(List)

📋 示例说明

示例 1:

复制代码
输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]

示例 2:

复制代码
输入:digits = ""
输出:[]

示例 3:

复制代码
输入:digits = "2"
输出:["a","b","c"]

💡 解题思路

这是一个典型的 回溯(Backtracking) 问题,也可以理解为 多叉树的深度优先遍历(DFS)

✅ 思路步骤(回溯法):

  1. 建立映射表 :将数字字符映射到对应的字母字符串(如 '2' → "abc")。
  2. 边界处理:若输入为空字符串,直接返回空列表。
  3. 递归回溯
    • 使用一个 path 字符串记录当前组合;
    • 每次从 digits 的第 index 位开始,取出对应数字的字母集合;
    • 遍历该集合中的每个字母,加入 path
    • 递归处理下一位数字;
    • 回溯时移除刚加入的字母(或使用不可变字符串避免显式回溯)。
  4. 终止条件 :当 index == digits.length() 时,说明已选完所有数字,将 path 加入结果集。

🔁 其他解法(可选):

  • BFS(广度优先搜索):逐层扩展组合,用队列维护中间结果。
  • 迭代法:从空字符串开始,每次对已有结果拼接新字母。

⚠️ 但回溯法最直观、简洁,且空间效率高(无需存储中间大量组合),是首选。


💻 代码实现

// Java 实现(回溯)

java 复制代码
class Solution {
    private static final Map<Character, String> phoneMap = new HashMap<Character, String>() {{
        put('2', "abc");
        put('3', "def");
        put('4', "ghi");
        put('5', "jkl");
        put('6', "mno");
        put('7', "pqrs");
        put('8', "tuv");
        put('9', "wxyz");
    }};

    public List<String> letterCombinations(String digits) {
        List<String> result = new ArrayList<>();
        if (digits == null || digits.length() == 0) {
            return result;
        }
        backtrack(result, new StringBuilder(), digits, 0);
        return result;
    }

    private void backtrack(List<String> result, StringBuilder path, String digits, int index) {
        // 终止条件
        if (index == digits.length()) {
            result.add(path.toString());
            return;
        }

        char digit = digits.charAt(index);
        String letters = phoneMap.get(digit);

        for (char letter : letters.toCharArray()) {
            path.append(letter);               // 做选择
            backtrack(result, path, digits, index + 1); // 递归
            path.deleteCharAt(path.length() - 1); // 撤销选择(回溯)
        }
    }
}

// Go 实现(回溯)

go 复制代码
func letterCombinations(digits string) []string {
	if len(digits) == 0 {
		return []string{}
	}

	phoneMap := map[byte]string{
		'2': "abc",
		'3': "def",
		'4': "ghi",
		'5': "jkl",
		'6': "mno",
		'7': "pqrs",
		'8': "tuv",
		'9': "wxyz",
	}

	var result []string
	var backtrack func(path string, index int)
	backtrack = func(path string, index int) {
		if index == len(digits) {
			result = append(result, path)
			return
		}

		letters := phoneMap[digits[index]]
		for i := 0; i < len(letters); i++ {
			backtrack(path+string(letters[i]), index+1)
		}
	}

	backtrack("", 0)
	return result
}

💡 Go 版本利用字符串不可变性,直接传新字符串,无需显式回溯。


🧪 示例演示

digits = "23" 为例:

复制代码
初始: path = "", index = 0
├─ 选 'a' → path="a", index=1
│   ├─ 选 'd' → path="ad" → index=2 → 加入结果
│   ├─ 选 'e' → path="ae" → 加入结果
│   └─ 选 'f' → path="af" → 加入结果
├─ 选 'b' → path="b", index=1
│   ├─ 'd' → "bd"
│   ├─ 'e' → "be"
│   └─ 'f' → "bf"
└─ 选 'c' → path="c", index=1
    ├─ 'd' → "cd"
    ├─ 'e' → "ce"
    └─ 'f' → "cf"

最终结果:["ad","ae","af","bd","be","bf","cd","ce","cf"]


✅ 答案有效性证明

  • 完备性:回溯遍历了每一位数字对应的所有字母,且组合长度等于输入长度,无遗漏。
  • 无重复:每条路径唯一,且不重复访问同一位置的不同字母(顺序固定)。
  • 边界正确
    • 空输入 → 返回空列表(符合示例2);
    • 单数字 → 返回其所有字母(符合示例3)。

因此,算法正确。


📊 复杂度分析

项目 分析
时间复杂度 O ( 3 m t i m e s 4 n ) O(3^m \\times 4^n) O(3mtimes4n),其中 m 是对应 3 个字母的数字个数(2,3,4,5,6,8),n 是对应 4 个字母的数字个数(7,9)。最坏情况每位都是 7 或 9,即 O ( 4 k ) O(4^k) O(4k),kdigits 长度(≤4)
空间复杂度 O ( k ) O(k) O(k),递归栈深度为 k(不计结果存储空间);若计入结果,则为 O ( 3 m t i m e s 4 n t i m e s k ) O(3^m \\times 4^n \\times k) O(3mtimes4ntimesk)

💡 由于 k ≤ 4,实际运行非常快。


📌 问题总结

  • 核心思想:回溯(DFS)生成所有组合。
  • 关键技巧
    • 建立数字到字母的映射;
    • 递归 + 回溯控制路径;
    • 注意空输入边界。
  • 适用场景:组合、排列、子集类问题。
  • 延伸思考:若允许重复数字或更长输入,需考虑剪枝或记忆化,但本题规模小,无需优化。

✅ 掌握此题,就掌握了回溯算法的经典模板!

github地址: https://github.com/swf2020/LeetCode-Hot100-Solutions

相关推荐
不想看见4041 小时前
Shortest Bridge -- 广度优先搜索 --力扣101算法题解笔记
算法·leetcode·宽度优先
流云鹤2 小时前
2026牛客寒假算法基础集训营5(B D G J F )
算法
教男朋友学大模型2 小时前
LoRA 为什么必须把一个矩阵初始化为0
人工智能·算法·面试·求职招聘
得一录2 小时前
Python 算法高级篇:布谷鸟哈希算法与分布式哈希表
python·算法·aigc·哈希算法
独自破碎E2 小时前
BISHI53 [P1080] 国王游戏(简化版)
android·java·游戏
啊吧啊吧abab2 小时前
二分查找与二分答案
c++·算法·二分
AC赳赳老秦2 小时前
2026 智能制造趋势:DeepSeek 助力“黑灯”工厂运营,实现生产流程自动化
网络·数据结构·算法·安全·web安全·prometheus·deepseek
坚持就完事了2 小时前
Java中的异常
java·开发语言
流云鹤2 小时前
2026牛客寒假算法基础集训营6(K H G B A)
算法