夯实算法-打家劫舍 III

130 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的29天,点击查看活动详情

题目:LeetCode

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

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

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

示例 1:

rob1-tree.jpg

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

示例 2:

rob2-tree.jpg

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

提示:

  • 树的节点数在 [1, 10410^4]范围内
  • 0<=Node.val<=1040 <= Node.val <= 10^4

解题思路

由题意分析属于典型动态规划问题,但它是树形结构,并不是常规的线性结构,如何理顺依赖关系是关键。

一开始是往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);
    }
}

运行结果

Snipaste_2022-12-27_18-55-45.png

复杂度分析

  • 空间复杂度:O(n)O(n)
  • 时间复杂度:O(n2)O(n^2)

掘金(JUEJIN)  一起分享知识, Keep Learning!