Medium
题目描述
小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root 。
除了 root 之外,每栋房子有且只有一个"父"房子与之相连。一番侦察之后,聪明的小偷意识到"这个地方的所有房屋的排列类似于一棵二叉树"。如果 两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。
给定二叉树的 root ,返回 在不触动警报的情况下 ,小偷能够盗取的最高金额。
示例 1:
输入: root = [3,2,3,null,3,null,1]
输出: 7
解释: 小偷一晚能够盗取的最高金额 3 + 3 + 1 = 7
示例 2:
输入: root = [3,4,5,1,3,null,1]
输出: 9
解释: 小偷一晚能够盗取的最高金额 4 + 5 = 9
提示:
- 树的节点数在
[1, 10^4]范围内 0 <= Node.val <= 10^4
解题思路
这是一道经典的树形动态规划问题。关键在于理解约束条件:不能同时偷取直接相连的两个节点。
思路分析
对于每个节点,我们有两种选择:
- 偷当前节点:那么左右子节点都不能偷,只能偷左右子树的孙子节点
- 不偷当前节点:可以选择偷或不偷左右子节点,取最优解
解法一:递归 + 记忆化(基础思路)
直接递归会有重复计算问题,用哈希表缓存结果。
解法二:优化的树形DP(推荐)
为每个节点维护两个状态:
rob[0]:不偷当前节点的最大收益rob[1]:偷当前节点的最大收益
状态转移方程:
rob[0] = max(left[0], left[1]) + max(right[0], right[1])rob[1] = node.val + left[0] + right[0]
这种方法一次遍历即可,无需额外存储空间,是最优解法。
代码实现
class Solution {
public:
int rob(TreeNode* root) {
auto result = dfs(root);
return max(result[0], result[1]);
}
private:
vector<int> dfs(TreeNode* node) {
if (!node) return {0, 0};
auto left = dfs(node->left);
auto right = dfs(node->right);
int notRob = max(left[0], left[1]) + max(right[0], right[1]);
int rob = node->val + left[0] + right[0];
return {notRob, rob};
}
};
class Solution:
def rob(self, root: Optional[TreeNode]) -> int:
def dfs(node):
if not node:
return [0, 0]
left = dfs(node.left)
right = dfs(node.right)
not_rob = max(left) + max(right)
rob = node.val + left[0] + right[0]
return [not_rob, rob]
return max(dfs(root))
public class Solution {
public int Rob(TreeNode root) {
var result = Dfs(root);
return Math.Max(result[0], result[1]);
}
private int[] Dfs(TreeNode node) {
if (node == null) return new int[] {0, 0};
var left = Dfs(node.left);
var right = Dfs(node.right);
int notRob = Math.Max(left[0], left[1]) + Math.Max(right[0], right[1]);
int rob = node.val + left[0] + right[0];
return new int[] {notRob, rob};
}
}
var rob = function(root) {
const dfs = (node) => {
if (!node) return [0, 0];
const left = dfs(node.left);
const right = dfs(node.right);
const notRob = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
const robCur = node.val + left[0] + right[0];
return [notRob, robCur];
};
const result = dfs(root);
return Math.max(result[0], result[1]);
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n) | 每个节点只访问一次,n为节点数量 |
| 空间复杂度 | O(h) | 递归栈深度,h为树的高度,最坏情况O(n) |
相关题目
. House Robber (Medium)
. House Robber II (Medium)