

打家劫舍 III 是 打家劫舍 系列问题中的一个变种。这里的房屋排列不再是线性排列,而是形成了一棵二叉树,每个节点代表一个房子,每个房子内有一定的钱。相邻的房子(即父子关系)不能同时被抢。我们的目标是找出在这棵二叉树中,能够抢劫到的最大金额。
给定一个二叉树结构,每个节点表示一个房子,房子里有一定的钱,要求我们在不抢劫相邻房子的情况下,抢劫到的最大金额。
对于每个节点,我们有两种选择:
root,我们定义两个状态: rob(root):表示抢劫当前节点时能够获得的最大收益。not_rob(root):表示不抢劫当前节点时能够获得的最大收益。root.val 加上不抢劫左右子节点的收益:
我们从树的根节点开始,递归地计算每个节点的抢劫和不抢劫情况下的最大收益。最终的结果是根节点抢劫和不抢劫情况下的最大值。
postorder(后序遍历)的方式,先计算子节点的收益,再计算父节点的收益。(0, 0),即抢劫和不抢劫的收益都是 0。function rob(root):
if root is None:
return (0, 0)
left = rob(root.left)
right = rob(root.right)
rob_current = root.val + left[1] + right[1]
not_rob_current = max(left[0], left[1]) + max(right[0], right[1])
return (rob_current, not_rob_current)
result = rob(root)
return max(result[0], result[1])O(h),其中 h 是树的高度。O(n),因为每个节点只被遍历一次。O(n),适合处理大规模的二叉树输入。以上就是打家劫舍 III问题的基本思路。
class Solution:
def rob(self, root: TreeNode) -> int:
# 返回 (抢劫当前节点的最大收益, 不抢劫当前节点的最大收益)
def helper(node):
if not node:
return (0, 0)
left = helper(node.left)
right = helper(node.right)
# 抢劫当前节点的收益
rob_current = node.val + left[1] + right[1]
# 不抢劫当前节点的收益
not_rob_current = max(left[0], left[1]) + max(right[0], right[1])
return (rob_current, not_rob_current)
# 计算根节点的抢劫与不抢劫的最大收益
result = helper(root)
# 返回两种情况中的最大值
return max(result[0], result[1])helper(node),返回两个值:抢劫当前节点的最大收益和不抢劫当前节点的最大收益。class Solution {
public:
pair<int, int> helper(TreeNode* node) {
if (!node) return {0, 0};
pair<int, int> left = helper(node->left);
pair<int, int> right = helper(node->right);
// 抢劫当前节点的最大收益
int rob_current = node->val + left.second + right.second;
// 不抢劫当前节点的最大收益
int not_rob_current = max(left.first, left.second) + max(right.first, right.second);
return {rob_current, not_rob_current};
}
int rob(TreeNode* root) {
pair<int, int> result = helper(root);
return max(result.first, result.second);
}
};helper(node),返回一对值,表示抢劫和不抢劫当前节点的最大收益。O(n),空间复杂度为 O(h),其中 n 是节点数量,h 是树的高度。