【LeetCode】46 and 112(回溯算法)

回溯算法

回溯算法其实就是我们常说的 DFS 算法,本质上就是⼀种暴⼒穷举算法。解决⼀个回溯问题,实际上就是⼀个决策树的遍历过程。你只需要思考3 个问题:
1、路径:也就是已经做出的选择。
2、选择列表:也就是你当前可以做的选择。
3、结束条件:也就是到达决策树底层,⽆法再做选择的条件。
代码⽅⾯,回溯算法的框架:

result = []
def backtrack(路径, 选择列表):
	if 满⾜结束条件:
		result.add(路径)
		return
	for 选择 in 选择列表:
		做选择
		backtrack(路径, 选择列表)
		撤销选择

其核⼼就是 for 循环⾥⾯的递归,在递归调⽤之前「做选择」,在递归调⽤之后「撤销选择」,特别简单。
本文通过「全排列」和DFS过程来讲述上述回溯框架。

46. 全排列

在这里插入图片描述
解法:回溯算法
全排列的穷举过程可以构造一个决策树。而全排列的计算就是对该决策树的遍历结果。
在这里插入图片描述
对于图中的红色节点所在状态::[2] 就是「路径」,记录你已经做过的选择;[1,3] 就是「选择列表」,表示你当前可以做出的选择;「结束条件」就是遍历到树的底层,在这⾥就是选择列表为空的时候。这可以解答开头介绍的⼏个名词。
如果明⽩了这⼏个名词,可以把「路径」和「选择」列表作为决策树上每个节点的属性,⽐如下图列出了⼏个节点的属性:
在这里插入图片描述
我们定义的 backtrack 函数其实就像⼀个指针,在这棵树上游⾛,同时要正确维护每个节点的属性,每当⾛到树的底层,其「路径」就是⼀个全排列。
「路径」和「选择」是每个节点的属性,函数在树上游⾛要正确维护节点的属性,那么就要在前序遍历(进入节点前)和后序遍历(出节点)搞点动作:
在这里插入图片描述

class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        subsets = []
        def dfs(nums, subset, used):
        	# 结束条件:子集长度等于原数组长度,表示一个排列
            if len(subset) == len(nums):
                temp = list(map(lambda x: nums[x], subset))
                # if temp not in subsets:
                subsets.append(temp)
                return 
            # 遍历所有选择
            for i in range(len(nums)):
            	# 未被选择
                if used[i] == 0:
                	# 做选择
                    subset.append(i)
                    used[i] = 1
                    dfs(nums, subset, used)
                    # 撤销选择
                    subset.pop()
                    used[i] = 0
        used = [0] * len(nums)
        dfs(nums, [], used)
        return subsets

112. 路径总和

在这里插入图片描述
在这里插入图片描述
解法:DFS,回溯算法
DFS其实就是一种回溯算法,本质上就是⼀种暴⼒穷举算法。对于该题,我们用DFS算法对每条路径的和进行计算,并判断其是否和目标值相等。

# 为了便于理解回溯框架的使用,我们在代码中详细列出了做选择的过程。
class Solution:
    def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
        # sumValue = 0
        if not root:
            return False
        def dfs(root, value):
            # nonlocal sumValue
            if not root:
                return False
            # 结束条件
            if not root.left and not root.right:
                return value == targetSum
            # 当前节点的选择列表为 left 和 right 中非空节点。
            # 下面代码显示遍历所有选择
            if root.left:
            	# 做出选择
                value += root.left.val
                left = dfs(root.left, value)
                # 撤销选择
                value -= root.left.val
            else:
                left = False
            if root.right:
            	# 做出选择
                value += root.right.val
                right = dfs(root.right, value)
                # 撤销选择
                value -= root.right.val
            else:
                right = False
            return left or right
        return dfs(root, root.val)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值