🌥(day57:P51)
好久没写题解文章了,今天重操旧业。
目录
📝题目:
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
0 <= digits.length <= 4
digits[i]
是范围['2', '9']
的一个数字。
电话上数字所对应的字母
⭐示例 1:
输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
⭐示例 2:
输入:digits = ""
输出:[]
⭐示例 3:
输入:digits = "2"
输出:["a","b","c"]
🚩题目分析:
题目看起来就是简单的排列组合,暴力解题未尝不可,但是直接使用for循环解题还是过于粗暴,比如当digits是二位数可以使用两重for循环,digits为三位数可以使用三重for循环,但是我们不知道输入的是digits几位数(当然你可以使用if语句判断digits是几位数就进行几重循环)。下面我们减少粗暴,更“优雅”解题。
💡解题思路:
🌟解法一:列表解析式🌟
我们还是运用排列组合的思想,首先先取出第一个数字中所有对应的字符
如2对应的字母:
a,b,b
接着再取下一个数字
如设下一数字为3,则3对应的字母:
d,e,f
那么我们将其全排列有:
['ad', 'ae', 'af', 'bd', 'be', 'bf', 'cd', 'ce', 'cf']
接着我们又取下一个数字
如设下一数字为4,则4对应的字母:
g,h,i
然后我们将g,h,i和上一次全排列获取到的结果进行排列组合
得:['adg', 'adh', 'adi', 'aeg', 'aeh', 'aei', 'afg', 'afh', 'afi', 'bdg', 'bdh', 'bdi', 'beg', 'beh', 'bei', 'bfg', 'bfh', 'bfi', 'cdg', 'cdh', 'cdi', 'ceg', 'ceh', 'cei', 'cfg', 'cfh', 'cfi']
细心的朋友可能已经发现了,这里的排列组合其实就相当于两重for循环。将下一个数字的对应的字母和上一次全排列的结果做全排列,我们就可以解决上述题目分析中未知digits位数而不知使用几重循环的问题。
🌈代码实现
class Solution(object):
def letterCombinations(self, digits):
dct = {'2': ['a', 'b', 'c'],
'3': ['d', 'e', 'f'],
'4': ['g', 'h', 'i'],
'5': ['j', 'k', 'l'],
'6': ['m', 'n', 'o'],
'7': ['p', 'q', 'r', 's'],
'8': ['t', 'u', 'v'],
'9': ['w', 'x', 'y', 'z']}
if digits == '':
return []
ans = ['']
for num in digits:
ans = [item+ap for item in ans for ap in dct[num]]
return ans
✏代码注释
class Solution(object):
def letterCombinations(self, digits):
dct = {'2': ['a', 'b', 'c'], # 用于映射数字和字母关系的字典
'3': ['d', 'e', 'f'],
'4': ['g', 'h', 'i'],
'5': ['j', 'k', 'l'],
'6': ['m', 'n', 'o'],
'7': ['p', 'q', 'r', 's'],
'8': ['t', 'u', 'v'],
'9': ['w', 'x', 'y', 'z']}
if digits == '': # digits为空,返回空列表
return []
ans = [''] # 初始化元素为字符型的列表
for num in digits: # 遍历输入的数字字符串
# 从ans取出item,ans为上一次排列组合的所有结果,item则为其中的一种结果
# 从dct[num]取出ap,dct[num]为当前数字对应的字母列表,ap则为其中的一个字母
# 列表解析式将其排列组合(等效于双重for循环)
ans = [item+ap for item in ans for ap in dct[num]]
return ans
🌟解法二:笛卡尔积🌟
笛卡尔积和排列组合的思路差不多,我们使用itertools库来实现笛卡尔积
我们直观先体验笛卡尔积的使用
import itertools a = [1, 2, 3] b = [4, 5] print("a,b的笛卡尔乘积:"), for x in itertools.product(a, b): print(x) print("a自身的笛卡尔乘积:"), for x in itertools.product(a, a): print(x)
输出
a,b的笛卡尔乘积:
(1, 4)
(1, 5)
(2, 4)
(2, 5)
(3, 4)
(3, 5)
a自身的笛卡尔乘积:
(1, 1)
(1, 2)
(1, 3)
(2, 1)
(2, 2)
(2, 3)
(3, 1)
(3, 2)
(3, 3)
(了解更多笛卡尔积大家可以自行搜索)
🌈代码实现
class Solution(object):
def letterCombinations(self, digits):
dct = {
"2": "abc",
"3": "def",
"4": "ghi",
"5": "jkl",
"6": "mno",
"7": "pqrs",
"8": "tuv",
"9": "wxyz",
}
if not digits:
return list()
groups = (dct[digit] for digit in digits)
return ["".join(item) for item in itertools.product(*groups)]
# ["".join(item) for item in itertools.product(*groups)]
# ans = []
# for item in itertools.product(*groups):
# temp = "".join(item)
# ans.append(temp)
# return ans
✏代码注释
class Solution(object):
def letterCombinations(self, digits):
dct = { # 数字和字母映射字典,和上面不同的是,字母不使用列表单个储存
"2": "abc",
"3": "def",
"4": "ghi",
"5": "jkl",
"6": "mno",
"7": "pqrs",
"8": "tuv",
"9": "wxyz",
}
if not digits: # 判断是否为空
return list()
groups = (dct[digit] for digit in digits) # 生成字母组合对象,如list(groups) =['abc', 'def', 'ghi']
return ["".join(item) for item in itertools.product(*groups)] # 进行组合并返回结果
# ["".join(item) for item in itertools.product(*groups)] 列表解析式等价于
# ans = [] # 结果列表
# for item in itertools.product(*groups): # item为('a', 'd', 'g')类型元组
# temp = "".join(item) # 将元组中的元素提取出来拼接成字符串
# ans.append(temp) # 将拼接好的组合字符串添加到结果列表
# return ans
这里简要介绍itertools.product(*groups)
*是python中一个赋值的技巧,叫做解包。相信很多人都见过def func(*args, **kwargs)这种写法,在函数中,*代表不定个数的参数,以tuple的方式传入。
product(*groups)接收若干个位置参数,转换成元组tuple形式
🌟解法三:回溯🌟
回溯算法用于寻找所有的可行解,如果发现一个解不可行,则会舍弃不可行的解。在这道题中,由于每个数字对应的每个字母都可能进入字母组合,因此不存在不可行的解,可以直接穷举所有的解即可。
回溯主要体现再1和2,从2回溯到1
如当a和d组合后得到一个结果,到达2d位置
接着我们回溯到1a位置,a再和组合得到另一个结果
🌈代码实现
class Solution:
def letterCombinations(self, digits):
dct = {
"2": "abc",
"3": "def",
"4": "ghi",
"5": "jkl",
"6": "mno",
"7": "pqrs",
"8": "tuv",
"9": "wxyz",
}
if not digits:
return list()
def backtrack(index):
if index == len(digits):
ans.append("".join(item))
else:
digit = digits[index]
for letter in dct[digit]:
item.append(letter)
backtrack(index + 1)
item.pop()
item = list()
ans = list()
backtrack(0)
return ans
✏代码注释
class Solution:
def letterCombinations(self, digits):
dct = {
"2": "abc",
"3": "def",
"4": "ghi",
"5": "jkl",
"6": "mno",
"7": "pqrs",
"8": "tuv",
"9": "wxyz",
}
if not digits: # digits为空的返回
return list()
def backtrack(index): # index为下标或索引
if index == len(digits): # 下标到达digits的长度说明以获取到一个完整字母组合
ans.append("".join(item)) # 将该字母组合添加到结果列表
else: # 如果没获取到一个完整字母组合则继续
digit = digits[index] # 取下一个数字
for letter in dct[digit]: # 遍历当前数字所对应的字母
item.append(letter) # 将该字母添加到item中(后)形如['a', 'd']
backtrack(index + 1) # 下标+1进入下一层递归
item.pop() # 回溯,删除当前组合最后一个字母,回到前一步,如从['a', 'd']删除'd',回到['a']
# 再进行 ['a', 'f']的组合
item = list() # 用于放置单个的字母组合列表
ans = list() # 放置所有组合列表
backtrack(0) # 调用backtrack(0)开始回溯
return ans # 返回结果
好了到本章到此结束,下期见