开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的29天,点击查看活动详情
题目:LeetCode
小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 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, ]范围内
解题思路
由题意分析属于典型动态规划问题,但它是树形结构,并不是常规的线性结构,如何理顺依赖关系是关键。
一开始是往DP方向想,可是示例越看越不对劲,想着偷父节点就能偷子节点,偷子节点就不能偷父节点,那不就是一层一层的偷吗?层序遍历,计算隔层的累和,然后取大的就可以了啊。很快撸好了一个层序遍历,示例也通过了。
题目说直接相连的不能一起偷,但也没有说就一定隔层偷啊,也可以隔两层啊?然后再试以层为单元的去dp,仍是WA。最终发现,其实同一层不可能是一致的(全偷或者全不偷)。
线性结构一般是最后是问题的解,从后往前递推,向前转移状态,然后从前向后计算。
树要怎么办,树是二维的啊,它有四个方向?应该从上往下,还是从下往上,还是从左到右,还是从右往左?
可以从最简单的来,比如如果偷了root,或者不偷root,会怎么样?有没有依赖,这样就能打开思路。如果偷了root,那就不能偷左右子节点,但可以偷四个孙子节点,这就产生了状态转移,也就是说root需要依赖它的子节点的状态。因此,需要从root向叶子递推,然后从下向上计算,也就是说要先计算好子节点的状态,这就需要后序遍历。
代码实现
class Solution {
Map <TreeNode, Integer> f;
Map <TreeNode, Integer> g;
public int rob(TreeNode root) {
f = new HashMap <> ();
g = new HashMap <> ();
dfs(root);
return Math.max(f.getOrDefault(root, 0), g.getOrDefault(root, 0));
}
private void dfs(TreeNode root) {
if (root == null) {
return;
}
dfs(root.left);
dfs(root.right);
int fo = root.val + g.getOrDefault(root.left, 0) + g.getOrDefault(root.right, 0);
f.put(root, fo);
int gl = Math.max(f.getOrDefault(root.left, 0), g.getOrDefault(root.left, 0));
int gr = Math.max(f.getOrDefault(root.right, 0), g.getOrDefault(root.right, 0));
g.put(root, gl + gr);
}
}
运行结果
复杂度分析
- 空间复杂度:
- 时间复杂度:
在掘金(JUEJIN) 一起分享知识, Keep Learning!