[LeetCode] 回溯算法

回溯法

回朔法的思想: 通过枚举法,对所有可能性进行遍历。 但是和枚举法不同的是回溯法不是一直遍历下去,而是在不满足条件是回退一步,去尝试其余的路,而回退的这一步就是回溯算法的关键。
因此回朔法可以简单的理解为: 走不通就退一步的枚举法。而这里回退点也叫做回朔点。
利用for循环和递归配合可以实现回朔: 当递归从递归出口出来之后,上一层的for循环就会继续执行,而for循环的继续执行就会给出当前节点下的下一条可行路径。然后再递归调用,就可以顺着这条从未走过的路径又向下走一步。
回溯模板 python

def dfs():
    if 回朔点:# 递归出口,这条路走到底了
        保存当前结果
        return  
    for route in all_route_set :  # 逐步选择当前节点下的所有可能route
         if 剪枝条件:
             剪枝前的操作
             return   # 不继续往下走了,退回上层,换个路走
         # 当前路径可能是条可行路径
         保存当前数据  # 向下走之前要记住已经走过这个节点了。例如push当前节点
         dfs() # 递归调用,继续向下走一步
         回朔清理     # 该节点下的所有路径都走完了,清理堆栈,准备下一个递归。例如pop当前节点

例题1 (力扣 39. 组合总和):
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。

class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        if len(candidates) == 0: return []
        candidates = sorted(candidates)  # 排序
        # 回溯法
        # candisates: 候选点列表, target:目标,begin:起始位置,path:当前走过的路径,res:结果列表
        def dfs(candidates, target, begin, path, res):
            path = path.copy()  # python 传的参数是引用,所以重新复制一份
            if target == 0: # 回溯点, 到达目标,该退出了
                res.append(path) # 保存当前结果
                return # 返回
            for i in range(begin, len(candidates)): # 从当前点的候选点中按顺序依次选取一个点进行回溯
                if target - candidates[i] < 0: # 如果此时已不可能在满足题意,则剪枝,返回
                    return 
                path.append(candidates[i]) # 回溯之前,记录当前点
                dfs(candidates, target-candidates[i], i, path, res) # 回溯
                # 注:此题数据可重复,所以回溯起点是i,否则为i+1
                path.pop() # 回溯清理,弹出当前点
        res = []
        dfs(candidates, target, 0, [], res)  # 回溯
        return res

20.9.18 补充一道题目 (全排列问题)

[力扣] 47. 全排列 II 。给定一个可包含重复数字的序列,返回所有不重复的全排列。

示例:
输入: [1,1,2]
输出:
[
  [1,1,2],
  [1,2,1],
  [2,1,1]
]

这是一道很经典的回溯题,采用回溯加上剪枝,可以达到很高的效率。

执行用时:40 ms, 在所有 Python3 提交中击败了98.20%的用户
内存消耗:13.3 MB, 在所有 Python3 提交中击败了97.76%的用户

思路: 从给定列表中每次取一个值,添加到path中,然后再从剩下的值中取一个,直到达到回溯条件(path长度等于nums长度),然后回溯。
在这道题中剪枝那一步是必要的,可以排除重复结果。

class Solution:
    def permuteUnique(self, nums: List[int]) -> List[List[int]]:
        n = len(nums)
        if n==0: return []  # 空列表直接返回
        nums.sort() # 排序
        def dfs(nums, path, res):
            if len(path) == n:  # 回溯点
                res.append(path[:])  # 添加到结果中,注意path和path[:]的群IE
                return
            for i in range(0, len(nums)): 
                if i>0 and nums[i]==nums[i-1]: # 剪枝,和上一次取值一样会造成重复,所以舍弃,重新取值
                    continue
                path.append(nums[i]) # 添加点
                dfs(nums[:i]+nums[i+1:], path, res) # 下一次选取值得nums是这一次旋球之后剩下的,也就是从nums中排除掉nums[i]
                path.pop() # 回溯一步,弹出一个点
        res = []
        dfs(nums, [], res)
        return res
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值