17. 电话号码的字母组合

726 阅读4分钟

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

image.png

示例 1:
输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]

解法一

自己的暴力求解(不推荐),因为题目中数字长度不超过4,所以分别讨论即可。

def letterCombinations1(digits):
    n = len(digits)
    if n == 0: return []
    ans = []
    d = {"2": "abc", "3": "def", "4": "ghi", "5": "jkl", "6": "mno", "7": "pqrs", "8": "tuv", "9": "wxyz"}
    if n == 1:
        for ch in d[digits]:
            ans.append(ch)
        return ans

    if n == 2:
        for ch1 in d[digits[0]]:
            for ch2 in d[digits[1]]:
                ans.append(ch1+ch2)
        return ans

    if n == 3:
        for ch1 in d[digits[0]]:
            for ch2 in d[digits[1]]:
                for ch3 in d[digits[2]]:
                    ans.append(ch1+ch2+ch3)
        return ans

    if n == 4:
        for ch1 in d[digits[0]]:
            for ch2 in d[digits[1]]:
                for ch3 in d[digits[2]]:
                    for ch4 in d[digits[3]]:
                        ans.append(ch1+ch2+ch3+ch4)
        return ans

解法二

回溯 参考力扣精选答案,当当题目中出现 “所有组合” 等类似字眼时,我们第一感觉就要想到用回溯。

主要思想: 使用递归,自顶向下的递归实现深搜,定义子问题,在当前递归层结合子问题结果解决原问题。

截屏2021-04-09 下午10.06.51.png

def letterCombinations3(digits):
    if not digits: return []  # 如果字符串为空,返回一个空数组
    d = {"2": "abc", "3": "def", "4": "ghi", "5": "jkl", "6": "mno", "7": "pqrs", "8": "tuv", "9": "wxyz"}
    def backtrack(combination, nextdigit):
        """
        递归函数
        @param combination: 前面数字的某一个可能字符串组合
        @param nextdigit: 剩下的数字
        """
        if len(nextdigit) == 0:
            # 先写递归出口
            # 递归出口,当后面没有数字了的时候,就将这种组合添加到数组里面
            res.append(combination)
        else:
            # 遍历剩下数字中第一个数字对应的字母
            for letter in d[nextdigit[0]]:
                # 调用递归函数回溯
                # 将这个数字的对应的字母letter拼接在组合后面,剩下的数字要除去第一个
                backtrack(combination + letter, nextdigit[1:])
    res = []
    backtrack("", digits)  # 调用递归函数,初始的组合为空字符串"",nextdigit为整个数字字符串digits
    return res

解法三

队列 在参考力扣精选答案代码之前,自己实现的代码,主要思想就是:每次遍历原来前面数字的组合,并再遍历新的数字对应的字符串,依次拼接在后面

def letterCombinations4(digits):
    if not digits: return []  # 如果字符串为空,返回一个空数组
    d = {"2": "abc", "3": "def", "4": "ghi", "5": "jkl", "6": "mno", "7": "pqrs", "8": "tuv", "9": "wxyz"}
    res = []  # 存储结果的数组
    for letter in digits:
        if len(res) == 0:
            # 遍历第一个数字对应的字母
            for ch in d[letter]:
                res.append(ch)
        else:
            # 用一个数组存储原来的前面数字所有可能的字母组合
            old_res = res.copy()
            # 遍历前面数字所有可能的字母组合
            for ch1 in old_res:
                # 将下一个数字对应的字母都分别加在前面数字所有可能字母组合的后面
                for ch2 in d[letter]:
                    res.append(ch1+ch2)
                # 删除原来的前面的字母字母组合
                res.remove(ch1)
    return res

力扣精选答案的做法是:先将输入的 digits 中第一个数字对应的每一个字母入队,然后将出队的元素与第二个数字对应的每一个字母组合后入队...直到遍历到 digits 的结尾。最后队列中的元素就是所求结果。

截屏2021-04-09 下午10.07.38.png

def letterCombinations5(digits):
    if not digits: return []  # 如果字符串为空,返回一个空数组
    phone = ['abc', 'def', 'ghi', 'jkl', 'mno', 'pqrs', 'tuv', 'wxyz']
    queue = [""]  # 初始化队列
    for digit in digits:
        for _ in range(len(queue)):
            tmp = queue.pop(0)  # tmp记录当前前面数字的一种字母组合,并删除
            # 这里我们不使用 int() 转换字符串,使用ASCII码
            # 将数字字符转为ASCII码,50是字符"2"的ASCII码
            for letter in phone[ord(digit) - 50]:
                queue.append(tmp + letter)  # 将tmp加上新的字母,组成组合添加到队列里面
    return queue
复杂度分析

上面算法的复杂度基本上都是一样的。

  • 时间复杂度:O(3M4N)O(3^M*4^N) 。M 是输入的数字字符串中对应三个字母的数字个数,N 是对应四个字母的数字个数。
  • 空间复杂度:O(3M4N)O(3^M*4^N) 。一共要生成 3M4N3^M * 4^N 个结果。