Python算法(CCF-CSP考试)——DFS深度优先遍历算法

深度优先遍历

从某个节点v出发进行搜索,直到该节点的所有边都遍历完。当该节点所有边被遍历完后,回溯到v的前驱节点,继续搜索这个前驱节点的其他边。

二叉树

二叉树属于树结构,每个节点有两个分支,成为“左子树”“右子树”,每一层最多 2^n-1个节点。

二叉树遍历顺序

先(前)序遍历:DLR,中序遍历:LDR,后序遍历:LRD

LDR分别为左子树、右子树、根

上图 先序遍历:A-B-D-C-E-F

三种遍历方式都属于深度遍历,广度优先顺序遍历:A-B-C-D-E-F

抓小偷问题(Python构建并调用二叉树结构)

题目:一个绝顶狡猾的小偷瞄准一个高档小区。小区的别墅以二叉树的结构坐落。除了第一栋别墅,每一栋别墅都与另一栋“源头”别墅连接。一旦小偷闯入两栋相接的别墅,警铃就会触发。一旦小偷闯入两栋相接的别墅,警铃就会被触发。二叉树节点存储的各别墅的财产,狡猾的小偷已经计划好了路线,装备在不触发警报的前提下偷最多的钱。别墅保安打算在小偷必经之路上布置警卫,不动声色地抓住小偷。

问题是:小偷会怎么选路线呢

如,下图中,小偷分别选画√处受益最大

思路 

如果我们把二叉树分层,小偷不能偷相邻两层的房子,偷了这一个节点,就不能偷连接的上下两层,每一个节点的取舍都关系到其他节点,所以小偷需要统筹全局。

规律——每一个节点的偷值都是:左侧子节点的不偷值+右侧子节点的不偷值+节点的财富值;每一个节点的不偷值都是:左侧子节点的最大值+右侧子节点的最大值

代码

class TreeNode(object):
	def __init__(self, x, left=None, right=None):
		self.value = x
		self.right = right
		self.left = left

	def rob(self, root):   # 该节点最大财富值
		a = self.helper(root)
		return max(a[0], a[1])

	def helper(self, root):   # 输出root节点的偷值与不偷值
		if(root == None):   # 当节点不存在(到底)
			return [0, 0]
		else:
			left = self.helper(root.left)   # 递归
			right = self.helper(root.right)
			robValue = root.value + left[1] + right[1]   # 该节点偷值
			skipValue = max(left[0], left[1]) + max(right[0], right[1])  # 不偷
			return [robValue, skipValue]


A = TreeNode(3)   
B = TreeNode(1)
C = TreeNode(2, None, A)
D = TreeNode(3, None, B)
E = TreeNode(3, C, D)
print(E.left.value)
print(E.rob(E))

 

二叉树中最大路径和 

思路

最大的路径和有可能是哪些情况。

第一种是左子树的路径加上当前节点
第二种是右子树的路径加上当前节点
第三种是左右子树的路径加上当前节点(相当于一条横跨当前节点的路径)
第四种是只有自己的路径。
这四种情况只是用来计算以当前节点根的最大路径。
如果当前节点上面还有节点,那它的父节点是不能累加第三种情况的。所以我们要计算两个最大值,

一个是当前节点下的最大路径和,

一个是如果要连接父节点时最大的路径和

我们用前者更新全局最大量,用后者返回递归值就行了。

经过某一节点的最大路径和就一定在这四种情况中 max( case_1,case_2,case_3,case_4 )
        ans = max(ans,left+right+root.val,left+root.val,right+root.val,root.val)
            联想以前做的maxPathSum,如果前导路径,or 左右子树的最大和,是负数,那就直接当没有左右子树好了
            也就是说left_max = max(0,getMax(root.left))
            所以上面的max比较中, 可以不去比较4个这么多,直接max(ans,left+right+root.val)就好了,这样就已经
        囊过上面的4种所有情况的比较了,此时便可以得到,如果以某个节点为最大路径和的一部分时的最大值
        ❗️❗️❗️:
            要注意函数的返回值,函数返回的是代表当前节点作为头部节点的情况下,子树路径的最大和,譬如下面这个树
                   -10
                   / \
                  9  20
                    /  \
                   15   7
            20这个节点,在计算其为最大路径一部分的时候,当然会考虑20在路径中了,
            也就是:左15 根20 右7
                但是由于路径上是不能有回溯的,每个节点一定是遍历一次,即不可能有-10 20 15 20 7 这样的路径,如果
            -10 要把20 15 7都囊括进来的话,是不对的。无法将路径拉成一条平的path,一条平的path遍历时一定是没有回溯
            关系的。比如9,-10,20,7或者9,-10,20,15这样才是合法的路径
                所以当前函数执行完时,应该返回max(left,right) + root.val,即以当前节点作为一段序列和的末尾,这个
            节点的左子树或者右子树作为其前导or(后导)路径,并且只能选择一条,所以是max(left,right) + root.val

代码

class TreeNode(object):
	def __init__(self, x, left=None, right=None):
		self.value = x
		self.right = right
		self.left = left

class Solution(object):

	def __init__(self):
		self.result = float("-inf")

	def maxPathSum(self, root:TreeNode):
		if root == None:
			return 0
		self.getMax(root)
		return self.result

	def getMax(self, root:TreeNode):
		if root == None:
			return 0
		left = max(0, self.getMax(root.left))   # 若正数则取,否则不取(0)
		right = max(0, self.getMax(root.right))
		self.result = max(self.result, left+right+root.value) 
		# 第二项为算上该节点的最大值,
		return max(right, left) + root.value
		# 返回的应该是该节点作为结尾点的最大值(不可回溯,所以要往上迭代)
		# 此时result>=返回值,因为result是可以直接当结果的
		# 而返回值需要进行下一步迭代,该节点不能为中间点,只能为端点

si = TreeNode(10)
wu = TreeNode(12)
liu = TreeNode(2)
qi = TreeNode(1)
er = TreeNode(2, si, wu)
san = TreeNode(-2, liu, qi)
yi = TreeNode(1, er, san)
a = Solution()
print(a.maxPathSum(yi))

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值