Go&Java算法之打家劫舍Ⅲ

129 阅读1分钟

这是我参与2022首次更文挑战的第17天,活动详情查看:2022首次更文挑战

打家劫舍Ⅲ

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

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

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

 

示例 1:

image.png

输入: root = [3,2,3,null,3,null,1]

输出: 7

解释: 小偷一晚能够盗取的最高金额 3 + 3 + 1 = 7

示例 2:

image.png

输入: root = [3,4,5,1,3,null,1]

输出: 9

解释: 小偷一晚能够盗取的最高金额 4 + 5 = 9  

提示:

树的节点数在 [1, 104] 范围内

0 <= Node.val <= 104

题解

算法一:动态规划(Java)

这道题目和打家劫舍、打家劫舍II 也是如出一辙,只不过这个换成了树。

如果对树的遍历不够熟悉的话,那本题就有难度了。

对于树的话,首先就要想到遍历方式,前中后序(深度优先搜索)还是层序遍历(广度优先搜索)。

本题一定是要后序遍历,因为通过递归函数的返回值来做下一步计算。

与打家劫舍、打家劫舍II 一样,关键是要讨论当前节点抢还是不抢。

如果抢了当前节点,两个孩子就不能动,如果没抢当前节点,就可以考虑抢左右孩子(注意这里说的是“考虑”)

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public int rob(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int[] res = rob3Helper(root);
        return Math.max(res[0], res[1]);
    }

    public int[] rob3Helper(TreeNode root) {
        if (root == null) {
            return new int[]{0, 0};
        }

        int[] left = rob3Helper(root.left);
        int[] right = rob3Helper(root.right);

        int select = root.val + left[0] + right[0];
        int noSelect = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);

        return new int[]{noSelect, select};
    }
}

时间复杂度:O(N)

空间复杂度:O(logN)

算法一:动态规划(Go)

思路同上

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func rob(root *TreeNode) int {
    traverse(root)
    return root.Val
}

func traverse(root *TreeNode) {
    if nil == root {
        return
    }
    traverse(root.Left)
    traverse(root.Right)
    left, right := 0, 0 //左右子树的值
    leftNext, rightNext := 0, 0 //左右子树的子树
    if nil != root.Left {
        left = root.Left.Val
        if nil != root.Left.Left {
            leftNext += root.Left.Left.Val
        }   
        if nil != root.Left.Right {
            leftNext += root.Left.Right.Val
        }
    }

    if nil != root.Right {
        right = root.Right.Val
        if nil != root.Right.Left {
            rightNext += root.Right.Left.Val
        }
        if nil != root.Right.Right {
            rightNext += root.Right.Right.Val
        }
    }
    //改写root值,代表dp,(根+左右子树的子树和 与 根左右子树和 的最大值)与(左子树的子树和+右子树 与 右子树的子树和+左子树的最大值)的最大值
    root.Val = max(max(leftNext+rightNext+root.Val, left+right), max(leftNext+right, rightNext+left))
}

func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

时间复杂度:O(N)

空间复杂度:O(logN)