vlambda博客
学习文章列表

动态规划(六)--打家劫舍III (二叉树版)

来源:LeetCode

难度:中等

描述:

        在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。

计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。


示例1:


示例2:

动态规划(六)--打家劫舍III (二叉树版)


题外话:继偷完一维数组和环形数组,咱们又要尝试偷一把二叉树了...,实在没想到小偷行业也这么卷动态规划(六)--打家劫舍III (二叉树版)动态规划(六)--打家劫舍III (二叉树版)


分析:题意就是给一颗二叉树,让咱们在不能同时选择相邻节点的情况下找到整棵树和的最大值,这里的相邻节点即为父子节点,每个节点咱们都可以选择偷或者不偷,假设咱们选择父节点,那么两个子节点就不能选,但是可以选孙子节点,根据这个特性,咱们可以得出如下两种解法



解题


方法一:递归法

        思路:由分析可知,咱们选了父节点,两个子节点就无法选择,但是可以选择孙子节点,所以该父节点当前能得到的最大值就是  父节点+孙子节点的值 VS 两个子节点值相加  中较大的那个,那么咱们采用递归方式遍历每个父节点的最大值即可


代码:

 1public int rob(TreeNode root) {
2        if (root == null) {
3            return 0;
4        }
5        //该节点值
6        int money = root.val;
7        if (root.left != null) {
8            //左边两个孙子节点
9            money += (rob(root.left.left) + rob(root.left.right));
10        }
11
12        if (root.right != null) {
13            //右边两个孙子节点
14            money += (rob(root.right.left) + rob(root.right.right));
15        }
16        //该节点值 = Math.max(该节点值+4个孙子节点值,两个儿子节点值)
17        return Math.max(money, rob(root.left) + rob(root.right));
18    }


        上面的代码很容易看出,递归的过程中咱们出现了大量的重复计算,如咱们计算每个节点的过程中,会把儿子及孙子节点都计算了,那么当儿子节点作为根节点时,又会计算到孙子节点...

        所以咱们可以通过用Map存储每个节点结果的方式来进行优化,这个就不实现了,有兴趣的同学可以自己实现


题外话:可算逮着个机会偷懒了


方法二:动态规划

思路:如分析所说,咱们每个节点可以选择偷或者不透,所以我们每个节点结果有如下两种情况

  1. 当前节点选择不偷:当前节点能偷到的最大钱数 = 左孩子能偷到的钱 + 右孩子能偷到的钱

  2. 当前节点选择偷:当前节点能偷到的最大钱数 = 左孩子选择自己不偷时能得到的钱 + 右孩子选择不偷时能得到的钱 + 当前节点的钱数

这里咱们用数组来每个节点保存偷和不偷的结果,0代表当前节点偷,1代表当前节点不偷


代码:

 1public int rob(TreeNode root) {
2    int[] res = robDp(root);
3    return Math.max(res[0], res[1]);
4}
5
6public int[] robDp(TreeNode root) {
7    if (root == null) {
8        //节点为空,返回为0空数组
9        return new int[2];
10    }
11    int[] dp = new int[2];
12
13    //左右孩子能偷到的钱数
14    int[] leftDp = robDp(root.left);
15    int[] rightDp = robDp(root.right);
16
17    //当前节点偷 = 当前节点值 +  左右节点不偷值
18    dp[0] = root.val + leftDp[1] + rightDp[1];
19    //当前节点不偷  =  左右节点的最大值之和
20    dp[1] = Math.max(leftDp[0], leftDp[1]) + Math.max(rightDp[0], rightDp[1]);
21    return dp;
22}


题外话:偷完二叉树,下一次可能要求咱们去偷 图 类型的房子了...


以上仅是个人思路解法,觉得还不错欢迎点赞关注分享


往期精彩推荐