1. 题目
读题
https://leetcode.cn/problems/house-robber-iii/description/
好的,我可以给你一个图形化的示例。假设我们有这样一个二叉树:
3
/ \
4 5
/ \ \
1 3 1
我们可以用一个表格来表示每个节点的f0和f1的值:
节点 | f0 | f1 |
---|---|---|
1 | 1 | 0 |
3 | 3 | 0 |
1 | 1 | 0 |
4 | 4 + max(0, 1) + max(0, 3) = 8 | max(1, 0) + max(3, 0) = 4 |
5 | 5 + max(0, 1) + max(0, 0) = 6 | max(1, 0) + max(0, 0) = 1 |
3 | 3 + max(4, 8) + max(1, 6) = 14 | max(8, 4) + max(6, 1) = 13 |
所以,最终的答案是max(f0(3), f1(3)) = max(14, 13) = 14。
考查点
这道题的考查点是:
- 如何用递归或动态规划来解决树形结构的问题。
- 如何定义合适的状态和状态转移方程来表示问题的最优解。
- 如何优化递归的效率,避免重复计算。
- 如何用不同的编程语言来实现算法。
2. 解法
思路
思路是这样的:
- 我们定义一个辅助函数helper,它接受一个树节点作为参数,返回一个长度为2的数组,表示抢劫或不抢劫该节点能得到的最大值。
- 如果节点为空,我们返回[0, 0],表示没有钱可抢。
- 如果节点不为空,我们先递归地计算它的左右子树的结果,分别存储在left和right数组中。
- 然后,我们计算抢劫当前节点的最大值,就是当前节点的值加上不抢劫左右子树的最大值,即root.val + left[1] + right[1]。
- 接着,我们计算不抢劫当前节点的最大值,就是抢劫或不抢劫左右子树的最大值之和,即Math.max(left[0], left[1]) + Math.max(right[0], right[1])。
- 最后,我们返回这两个值组成的数组。
这道题的状态转移方程是:
f_0(root) = root.val + f_1(root.left) + f_1(root.right)
f_1(root) = max(f_0(root.left), f_1(root.left)) + max(f_0(root.right), f_1(root.right))
其中,
$f_0(root)$表示抢劫当前节点的最大值,
$f_1(root)$表示不抢劫当前节点的最大值。
具体实现
public int rob(TreeNode root) { if (root == null) return 0; int[] result = helper(root); return Math.max(result[0], result[1]); } public int[] helper(TreeNode root) { if (root == null) return new int[2]; int[] result = new int[2]; int[] left = helper(root.left); int[] right = helper(root.right); // result[0]表示抢劫当前节点的最大值 result[0] = root.val + left[1] + right[1]; // result[1]表示不抢劫当前节点的最大值 result[1] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]); return result; }