图解题库 / 回溯 · 枚举

17. 电话号码的字母组合

中等 含交互动画

学完这道你应该能: 说清它为什么用「枚举」,而不是死记步骤; 合上页面,自己默写出核心代码; 用 30 秒把思路讲清楚。

题目描述

给一串数字(2-9),按九宫格键盘映射,返回所有可能的字母组合。

digits = "23"
映射 = 2→abc, 3→def
输出 = ad ae af bd be bf cd ce cf

思路解析

两位写两层 for,五位写五层……位数是变量时,又卡在「层数不固定」上了。

把「定每一位」交给递归:当前位每选一个字母,就往下去定下一位,所有位都定完就收一条。

先建好键盘映射 {2:abc, 3:def, ...}。递归到第 i 位时,就去枚举 digits[i] 对应的那几个字母。

第 1 位 "2" → 选 a:第一位是 2,候选 a/b/c。先选 a,路径 "a",接着去定第二位。

第 2 位 "3" → 选 d:第二位是 3,候选 d/e/f。选 d,路径 "ad",两位都定完了,收进结果

回溯:a 配 e、配 f:退回第二位,把 e、f 也试一遍,收下 "ae"、"af"。a 打头的就全了。

换第一位:b、c 打头:再回到第一位换成 b、c……最终 9 种组合全部收齐

「多个位置、每个位置有几种选法、求全部搭配」的通用解法:一层递归管一位,到底就收。

参考代码(Python)

Python
1def letterCombinations(digits):
2    if not digits: return []
3    M = {class="cl-str">"2":class="cl-str">"abc",class="cl-str">"3":class="cl-str">"def",class="cl-str">"4":class="cl-str">"ghi",class="cl-str">"5":class="cl-str">"jkl",
4         class="cl-str">"6":class="cl-str">"mno",class="cl-str">"7":class="cl-str">"pqrs",class="cl-str">"8":class="cl-str">"tuv",class="cl-str">"9":class="cl-str">"wxyz"}
5    res = []
6    def bt(i, path):
7        if i == len(digits):              # 每位都定完了
8            res.append(class="cl-str">"".join(path)); return
9        for ch in M[digits[i]]:           # 枚举这一位的字母
10            bt(i + 1, path + [ch])        # 去定下一位
11    bt(0, [])
12    return res

复杂度分析

  • 时间复杂度:O(4ⁿ · n) —— n 位、每位最多 4 字母
  • 空间复杂度:O(n) —— 递归深度 = 位数

套路模板

骨架:用 i 标记当前是第几位,到底就收,否则枚举本位候选、递归 i+1。位数不定的「笛卡尔积」全用它。

Python
1def bt(i, path):
2    if i == 总位数:
3        res.append(path 的快照); return
4    for 选项 in 第 i 位的候选:
5        bt(i + 1, path + [选项])

易错点

  • digits 为空时返回 [""] → ✅ 空输入直接返回 [](空串没有任何组合,返回 [""] 会多一个假结果)
  • 把 7、9 当成 3 个字母 → ✅ 7→pqrs、9→wxyz 是 4 个(映射表照键盘抄准,7 和 9 各有 4 个字母,写错就漏组合)
  • 终止条件用 i == len(digits)-1 → ✅ i == len(digits)(定完最后一位后 i 会到 len(digits),那一刻才该收)

以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握

下一题 →51. N 皇后 ← 返回题库