- 思路
如果两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。入口只有root,但是开始偷的地方不一定是在root,因此结合二叉树的遍历和动态规划,每个节点依然只有偷或不偷两种状态。
计划采用前序递归进行二叉树遍历。
写出来一版代码,结果有问题。
本题一定是要后序遍历,因为通过递归函数的返回值来做下一步计算。因为最后实际是从root返回的,因此要从下晚上累计。
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func max(a, b int) int {
if a > b {
return a
}
return b
}
func rob(root *TreeNode) int {
if root == nil {
return 0
}
if root.Left == nil && root.Right == nil {
return root.Val
}
curMax, yesMax, noMax := 0, root.Val, 0
// 偷本节点
if root.Left != nil {
yesMax += rob(root.Left.Left) + rob(root.Left.Right)
}
if root.Right != nil {
yesMax += rob(root.Right.Left) + rob(root.Right.Right)
}
// 不偷本节点
noMax = rob(root.Left) + rob(root.Right)
curMax = max(yesMax, noMax)
return curMax
}
答案对,但是超时。计算了root的四个孙子(左右孩子的孩子)为头结点的子树的情况,又计算了root的左右孩子为头结点的子树的情况,计算左右孩子的时候其实又把孙子计算了一遍。
- 参考题解
可以使用一个map来记录已经计算的值。
这里看到动态规划的解答。
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func max(a, b int) int {
if a > b {
return a
}
return b
}
func robTree(root *TreeNode) []int {
// 长度为2的数组,0:不偷,1:偷
if root == nil {
return []int{0, 0}
}
if root.Left == nil && root.Right == nil {
return []int{0, root.Val}
}
// 左
left := robTree(root.Left)
// 右
right := robTree(root.Right)
// 中
// 偷(取偷本节点值加上不偷左节点和右节点)
// 偷了本节点,一定不能偷子节点
val1 := root.Val + left[0] + right[0]
// 不偷(不偷本节点,取偷左节点或不偷左节点的更大值)
// 因为不偷本节点,不一定要偷子节点,可能不偷
val2 := max(left[0], left[1]) + max(right[0], right[1])
return []int{val2, val1}
}
func rob(root *TreeNode) int {
result := robTree(root)
return max(result[0], result[1])
}