1. 216. 组合总和 III
1.1题目及讲解
找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:
- 只使用数字1到9
- 每个数字 最多使用一次
返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。
题目链接/文章讲解:https://programmercarl.com/0216.%E7%BB%84%E5%90%88%E6%80%BB%E5%92%8CIII.html
视频讲解:https://www.bilibili.com/video/BV1wg411873x
1.2代码实现
思路:
- 和part01中的77.组合问题相比,增加了和为n,数字范围从n变成了1-9
- 本题k相当于树的深度,9(因为整个集合就是9个数)就是树的宽度。
方法:
- 确定递归函数参数
- 需要一维数组path来存放符合条件的结果,二维数组result来存放结果集,可以定义为全局变量
- 接下来还需要如下参数:
- targetSum(int)目标和,也就是题目中的n。
- k(int)就是题目中要求k个数的集合。
- sum(int)为已经收集的元素的总和,也就是path里元素的总和。
- startIndex(int)为下一层for循环搜索的起始位置。
res = []
path = []
def back(target_sum,k,startindex,nums_sum):
- 确定终止条件
- 要找k个数,所以当找到的结果长度为k时就return,其中长度为k的集合和等于target_sum,就是要找的符合条件的结果,需要放到结果集中
if len(path) == k:
if target_sum == nums_sum:
res.append(path[:])
return
- 单层搜索过程
- 和77.组合问题相同,只是需要将n改成9,遍历范围固定为1-9
- 处理过程就是 path收集每次选取的元素,相当于树型结构里的边,sum来统计path里元素的总和。
- 处理过程和回溯过程是一一对应的,处理有加,回溯就要有减
for i in range(startindex,10-(k-len(path))+1):
nums_sum += i
path.append(i)
back(target_sum,k,i+1,nums_sum)
nums_sum -= i
path.pop()
- 剪枝操作
- 与组合问题相同,如果for循环选择的起始位置之后的元素个数 已经不足我们需要的元素个数了,那么就没有必要搜索了。意代码中i,就是for循环里选择的起始位置。
- 根据和剪枝,如果和(nums_sum)已经超过了target_sum,那么就没必要继续搜索了
1.2代码实现
res = []
path = []
def back(target_sum,k,startindex,nums_sum):
if nums_sum > target_sum: # 根据和剪枝
return
if len(path) == k:
if target_sum == nums_sum:
res.append(path[:])
return
for i in range(startindex,10-(k-len(path))+1):
nums_sum += i
path.append(i)
back(target_sum,k,i+1,nums_sum)
nums_sum -= i
path.pop()
k = 3
n = 7
nums_sum = 0
back(n,k,1,nums_sum)
print(res)
2. 17. 电话号码的字母组合
2.1题目及讲解
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
题目链接/文章讲解:https://programmercarl.com/0017.%E7%94%B5%E8%AF%9D%E5%8F%B7%E7%A0%81%E7%9A%84%E5%AD%97%E6%AF%8D%E7%BB%84%E5%90%88.html
视频讲解:https://www.bilibili.com/video/BV1yV4y1V7Ug
2.2代码实现
思路:
- 从示例上来说,输入"23",最直接的想法就是两层for循环遍历了吧,正好把组合的情况都输出了
- 但是题目并没有说输入的字符个数,也就是几层for循环并不确定,此时就可以用递归代替多层for循环,每递归一层代表使用了一层for循环
- 由于输入的是字符串,实际要使用的是字符串的数字对应的字母,所以先要处理数字和字母的映射关系
- 处理数字与字母的映射关系
使用数组存放对应字母构成的字符串,数组下标对应到数字,这样输入数字2,那么只需要在数字中找下标为2的字符串 - 由于字母来源是不同的字符串,所以在单层遍历时每次都要从0遍历到字符串最后一位,也就不需要startindex这个参数了
- 根据树形结构可以看出,深度方向决定了取不同的字符串,而取哪一个字符串又取决于输入的数字,取“23”的第一位表示在递归第一层,取第二位则进入第二层递归,通过数字索引来控制递归函数
digits = "234"
arr = ['', '', 'abc', 'def', 'ghi', 'jkl', 'mno', 'pqrs', 'tuv', 'wxyz']
res = []
path = []
n = len(digits)
def back(index):
if len(path) == n:
res.append(''.join(path[:]))
return
# index表示指向digits的索引,digits[index]则表示对应的数字,arr[int(digits[index])]表示数字对应的字符串
tmp = arr[int(digits[index])]
for i in tmp:
path.append(i)
back(index+1)
path.pop()
back(0)
print(res)