打家劫舍 III

721 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第10天,点击查看活动详情

打家劫舍 III

小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root 。

除了 root 之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果 两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。

给定二叉树的 root 。返回 在不触动警报的情况下 ,小偷能够盗取的最高金额 。

 

示例 1:

image.png

输入: root = [3,2,3,null,3,null,1] 输出: 7 解释: 小偷一晚能够盗取的最高金额 3 + 3 + 1 = 7

示例 2:

image.png

输入: root = [3,4,5,1,3,null,1] 输出: 9 解释: 小偷一晚能够盗取的最高金额 4 + 5 = 9   提示:

树的节点数在 [1, 104] 范围内 0 <= Node.val <= 104

内心os:这个小偷也太能偷了吧!

解题思路

看到这题,我的第一想法是后序遍历。

为什么是后序遍历?

使用后序遍历的话,为我们可以从低往上确定一个事情!

就是我们最底部的根节点,到他的节点,比对他的左右节点和他的根节点的大小,然后我们可以根据该情况选择需要使用哪一个节点?

首先实现后序遍历

不熟悉前序、后序、中序遍历的同学,可以稍微瞅瞅柿子带你刷 二叉树(leetcode实战篇)

// 按照左、右、根的顺序来就好
const postorder = (root,res) => {
  if(root === null){
    return
  }
  postorder(root.left, res)
  postorder(root.right, res)
  res.push(root.val)
}

在我们掏出这个公式之后,我们的任务就已经算是完成了一小半了。

接着处理我们的后序遍历公式

  1. 根据题意,我们可以知道,在我们选择偷 节点A的时候,节点A的左右两个子节点是不能偷的。
  2. 根据我们之前处理从佰草园(打家劫舍)到三味书屋(打家劫舍II)的方法,我们还是需要根据根节点的情况进行选、或者不选
    • 那么我们递归之后,可以返回两个数据一个是选择的值,一个是不选择的值 [select, onSelect]
  3. 选中目前节点的时候的值的表示:
    • root.val + left[onSelect] + right[onSelect]
  4. 不选中当前节点的时候,我们可以选中子节点或不选子节点
    • Math.max(left[select],left[onSelect]) + Math.max(right[select],right[onSelect]) 有了这个思路之后,我们就可以根据情况进行修改我们的公式了:
// 按照左、右、根的顺序来就好
const postorder = (root) => {
  // 首先遇到如果不需要进行递归了 那么这个节点就没有选中和不选中的情况需要进行考虑了
  // 直接返回 [0,0]
  if(root === null){
    return [0,0]
  }
  let left = postorder(root.left)
  let right = postorder(root.right)
  const select = root.val + left[1] + right[1]
  const onSelect= Math.max(...left)+Math.max(...right)
  return [select, onSelect]
}

完整代码

var rob = function(root) {
  const res = postorder(root);
  return Math.max(res[0], res[1]);
};
// 按照左、右、根的顺序来就好
const postorder = (root) => {
  // 首先遇到如果不需要进行递归了 那么这个节点就没有选中和不选中的情况需要进行考虑了
  // 直接返回 [0,0]
  if(root === null){
    return [0,0]
  }
  let left = postorder(root.left)
  let right = postorder(root.right)
  const select = node.val + left[1] + right[1]
  const onSelect= Math.max(...left)+Math.max(...right)
  return [select, onSelect]
}

这道题,最麻烦的一点是,我们需要理清楚在递归的过程中,选中的关系。

选中当前节点 node1 的时候,我们的当前值是多少,然后需要 node1+左子树不选中的值+右子树不选中的值。

然后还需要进行对应的值比对!

不过最后我只能感叹一句,流氓不可怕,就怕流氓有文化

最后,端午节快乐!