【代码随想录】d25-回溯算法-part02-python

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个数)就是树的宽度。
    在这里插入图片描述
    方法:
  1. 确定递归函数参数
  • 需要一维数组path来存放符合条件的结果,二维数组result来存放结果集,可以定义为全局变量
  • 接下来还需要如下参数:
    • targetSum(int)目标和,也就是题目中的n。
    • k(int)就是题目中要求k个数的集合。
    • sum(int)为已经收集的元素的总和,也就是path里元素的总和。
    • startIndex(int)为下一层for循环搜索的起始位置。
res = []
path = []
def back(target_sum,k,startindex,nums_sum):
  1. 确定终止条件
  • 要找k个数,所以当找到的结果长度为k时就return,其中长度为k的集合和等于target_sum,就是要找的符合条件的结果,需要放到结果集中
if len(path) == k:
    if target_sum == nums_sum:
        res.append(path[:])
    return
  1. 单层搜索过程
  • 和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()
  1. 剪枝操作
  • 与组合问题相同,如果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循环
  • 由于输入的是字符串,实际要使用的是字符串的数字对应的字母,所以先要处理数字和字母的映射关系
  1. 处理数字与字母的映射关系
    使用数组存放对应字母构成的字符串,数组下标对应到数字,这样输入数字2,那么只需要在数字中找下标为2的字符串
  2. 由于字母来源是不同的字符串,所以在单层遍历时每次都要从0遍历到字符串最后一位,也就不需要startindex这个参数了
  3. 根据树形结构可以看出,深度方向决定了取不同的字符串,而取哪一个字符串又取决于输入的数字,取“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)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值