算法训练营 day19 | 回溯算法part01

继续坚持刷题吧,准备刷完题去实习。冲冲冲!!!有好多二叉树的题跳过了,先不刷了。

 先丢一个回溯的模版

def backtracking(parameters):
    # 终止条件
    if termination_condition:
        # 存放结果
        store_result()
        return

    # 遍历当前层的选择集合
    for choice in choices:
        # 处理当前节点
        process_choice(choice)
        # 递归调用,继续选择
        backtracking(path, choice_list)
        # 回溯,撤销处理结果
        undo_choice(choice)


77. 组合

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        result = []
        self.backtracking(n, k, 1, [], result)
        return result
    
    def backtracking(self, n, k, startIndex, path, result):
        if len(path) == k: # path是已经选了的元素
            result.append(path[:])
            return
        
        for i in range(startIndex, n + 1 ): 
            path.append(i)
            self.backtracking(n, k, i + 1, path, result)
            path.pop()

可以进行剪枝,在遍历当前层的选择时可以缩小范围。 就像下面这张图。

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        result = []
        self.backtracking(n, k, 1, [], result)
        return result
    
    def backtracking(self, n, k, startIndex, path, result):
        if len(path) == k: # path是已经选了的元素
            result.append(path[:])
            return
        
        for i in range(startIndex, n - (k - len(path)) + 2):  # 剪枝只修改了这一行
            path.append(i)
            self.backtracking(n, k, i + 1, path, result)
            path.pop()

总结:见到回溯类的题目要先想到模版,然后先写出来基础版本之后再去考虑是否可以剪枝的问题。加油。

216. 组合总和 III

class Solution:
    def combinationSum3(self, k: int, n: int) -> List[List[int]]:
        result = []
        self.backtracking(n, k, 1, [], result)
        return result

    
    def backtracking(self, n, k, startIndex, path, result):
        if sum(path) > n: # 剪枝操作
            return
        if sum(path) == n and len(path) == k:
            result.append(path[:])
            return 
        
        for i in range(startIndex, 9 - (k - len(path)) + 2):
            path.append(i)
            self.backtracking(n, k, i + 1, path, result)
            path.pop()

总结:需要做剪枝,不然会TLE,整体和上一题的代码十分类似。只需要在加和大于目标值时直接返回就行。

17. 电话号码的字母组合

class Solution:
    def letterCombinations(self, digits: str) -> List[str]:
        if not digits:
            return []  # 如果输入为空,直接返回空列表
        
        path = []  # 当前路径存储组合
        result = []  # 存储最终结果
        hs = {'2':'abc', '3':'def', '4':'ghi', '5':'jkl',
              '6':'mno', '7':'pqrs', '8':'tuv', '9':'wxyz'}  # 数字到字母映射
        
        # 回溯函数
        def backtracking(index):
            # 如果当前路径长度等于输入数字的长度,则找到一个组合
            if len(path) == len(digits):
                result.append(''.join(path))  # 将路径拼成字符串并加入结果
                return
            
            # 获取当前数字对应的字母
            tmp = hs[digits[index]]
            # 遍历每个字母
            for i in range(len(tmp)):
                path.append(tmp[i])  # 加入当前字母
                backtracking(index + 1)  # 处理下一个数字
                path.pop()  # 回溯,移除当前字母
        
        backtracking(0)  # 从第一个数字开始回溯
        return result

总结:都在注释里了

39. 组合总和

class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        """
        寻找所有可能的组合,使得组合中的数字之和等于目标值 target。
        每个数字可以在组合中被重复选取多次。
        """
        result = []  # 存储所有满足条件的组合
        path = []    # 用于存储当前路径,也就是一个可能的组合

        def backtracking(startindex: int):
            """
            回溯函数,用于递归地生成所有组合。
            
            :param startindex: 表示开始选择数字的索引,避免重复选择之前已经选择过的数字。
            """
            # 如果当前路径中数字的和等于目标值,保存这个组合
            if sum(path) == target:
                result.append(path[:])  # 深拷贝当前路径并加入结果集
                return  # 找到一个解之后返回,不再继续递归

            # 如果当前路径中数字的和已经超过目标值,提前返回,避免无效递归
            if sum(path) > target:
                return  # 当前组合不符合条件,返回上层调用

            # 遍历候选数字,从当前索引开始,防止重复组合
            for i in range(startindex, len(candidates)):
                path.append(candidates[i])  # 选择当前数字,加入路径
                backtracking(i)  # 递归地调用回溯函数,允许重复使用当前数字
                path.pop()  # 回溯:撤销上一步选择,移除当前数字

        backtracking(0)  # 从索引 0 开始递归查找
        return result  # 返回所有符合条件的组合

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值