小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root 。
除了 root 之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果 两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。
给定二叉树的 root 。返回 在不触动警报的情况下 ,小偷能够盗取的最高金额 。
示例 1:
输入: root = [3,2,3,null,3,null,1]
输出: 7
解释: 小偷一晚能够盗取的最高金额 3 + 3 + 1 = 7
示例 2:
输入: root = [3,4,5,1,3,null,1]
输出: 9
解释: 小偷一晚能够盗取的最高金额 4 + 5 = 9提示:
树的节点数在 [1, 10410^4104] 范围内
0 <= Node.val <= 10410^4104
思想来自官方:https://leetcode.cn/problems/house-robber-iii/solution/da-jia-jie-she-iii-by-leetcode-solution/
简化一下这个问题:一棵二叉树,树上的每个点都有对应的权值,每个点有两种状态(选中和不选中),问在不能同时选中有父子关系的点的情况下,能选中的点的最大权值和是多少。
我们可以用 f(o)f(o)f(o) 表示选择 ooo 节点的情况下,ooo 节点的子树上被选择的节点的最大权值和;g(o)g(o)g(o) 表示不选择 ooo 节点的情况下,ooo 节点的子树上被选择的节点的最大权值和;lll 和 rrr 代表 ooo 的左右孩子。
-
当 ooo 被选中时,ooo 的左右孩子都不能被选中,故 ooo 被选中情况下子树上被选中点的最大权值和为 lll 和 $r $ 不被选中的最大权值和相加,即 f(o)=g(l)+g(r)f(o) = g(l) + g(r)f(o)=g(l)+g(r)。
-
当 ooo 不被选中时, ooo 的左右孩子可以被选中,也可以不被选中。对于 ooo 的某个具体的孩子 xxx,它对 ooo 的贡献是 xxx 被选中和不被选中情况下权值和的较大值。故 g(o)=max{f(l),g(l)}+max{f(r),g(r)}g(o) = \max \{ f(l) , g(l)\}+\max\{ f(r) , g(r) \}g(o)=max{f(l),g(l)}+max{f(r),g(r)}。
至此,我们可以用哈希表来存 fff 和 ggg 的函数值,用深度优先搜索的办法后序遍历这棵二叉树,我们就可以得到每一个节点的 fff和 ggg。根节点的 fff 和 ggg 的最大值就是我们要找的答案。
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def __init__(self):
self.f = {None: 0}
self.g = {None: 0}
def rob(self, root: TreeNode) -> int:
self.dfs(root)
return max(self.f[root], self.g[root])
def dfs(self, node: TreeNode):
if not node:
return
self.dfs(node.left)
self.dfs(node.right)
self.f[node] = node.val + self.g[node.right] + self.g[node.left]
self.g[node] = max(self.f[node.left], self.g[node.left]) + max(self.f[node.right], self.g[node.right])
if __name__ == '__main__':
s = Solution()
print(s.rob(TreeNode(3,TreeNode(2,None, TreeNode(3)), TreeNode(3, None, TreeNode(1)))))