337\. 打家劫舍 III

# 337. 打家劫舍 III (opens new window)

# Description

Difficulty: 中等

Related Topics: (opens new window), 深度优先搜索 (opens new window), 动态规划 (opens new window), 二叉树 (opens new window)

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

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

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

示例 1:

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

示例 2:

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

提示:

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

# Solution

Language: JavaScript

解题思路

在二叉树上进行动态规划。

对于二叉树,首先要确定遍历方式,本题用后序遍历,因为要确定是否要偷盗该节点,如果不偷,那么就可以考虑偷子节点。

所以dp数组就是一个长度为2的数组,第一个元素是不偷这个节点的金额,第二个元素是偷这个节点的金额。

于是递推公式就是,当不偷这个节点时,将左子节点返回的最大值和右子节点返回的最大值相加,即为不偷该节点可以获得的金额。如果要偷这个节点,就将这个节点的金额和不偷左右子节点的金额相加。

递归的返回条件就是当节点为空时,返回[0, 0]

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
var rob = function(root) {
    const postOrder = function (node) {
        if(!node) {
            return [0, 0];
        }
        const left = postOrder(node.left);
        const right = postOrder(node.right);
        const DoNot = Math.max(...left) + Math.max(...right);
        const Do = node.val + left[0] + right[0];
        return [DoNot, Do];
    }
    const result = postOrder(root);
    return Math.max(...result);
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

var rob = function(root) {
    let res = dfs(root);
    return Math.max(res[0], res[1]);
};

function dfs(root) {
    // res[0]表示不包括根节点的最大值,res[1]为包含根节点的最大值
    let res = [0,0];
    if (root === null) return res;
    let left = dfs(root.left);
    let right = dfs(root.right);
    // 不包含根节点的最大值为左子树最大值加右子树最大值
    res[0] = Math.max(...left) + Math.max(...right);
    // 包含根节点的最大值为当前节点值加左子树包含根节点的值加右子树包含根节点的值
    res[1] = root.val + left[0] + right[0];
    return res;
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
	const rob = root => {
	  // 后序遍历函数
	  const postOrder = node => {
	    if (!node) return [0, 0];
	
	    const left = postOrder(node.left);
	    const right = postOrder(node.right);
	
	    // 不偷当前节点,左右子节点都可以偷或不偷,取最大值
	    const DoNot = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
	    // 偷当前节点,左右子节点只能不偷
	    const Do = node.val + left[0] + right[0];
	
	    // [不偷,偷]
	    return [DoNot, Do];
	  };
	  
	  const res = postOrder(root);
	  // 返回最大值
	  return Math.max(...res);
	};


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
上次更新: 2022/8/25 下午2:49:04