63.打家劫舍III

一、题目描述

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

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

在这里插入图片描述

提示:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 1000

二、解题思路

对于树型结构,第一个就是想到的就是遍历方式,前中后(深度优先搜索),还是层次遍历(广度优先搜索)。

本题这里使用后续遍历,是因为子节点的状态陆续向上转移,最后在根节点汇总值。

第一步:确定dp数组(dp table)以及下标的含义

本题中dp的下标有两种表示:下标为0记录不偷该节点所得到的的最⼤⾦钱,下标为1记录偷该节点所得到的,的最⼤⾦钱。所以本题中的dp是一个长度为2的数组。

  • dp[0]: 不偷该节点所得到的的最⼤⾦钱
  • dp[1]: 偷该节点所得到的

第二步:终止条件

在遍历的过程中,如果遇到空间点的话,很明显,⽆论偷还是不偷都是0,所以就返回

if(node==null){
    return new int[]{0, 0};
}

第三步:确定遍历顺序

⾸先明确的是使⽤后序遍历。 因为通过递归函数的返回值来做下⼀步计算。

  • 通过递归左节点,得到左节点偷与不偷的⾦钱。

  • 通过递归右节点,得到右节点偷与不偷的⾦钱。

int[] left = robTree(node.left);
int[] right = robTree(node.right);

第四步:确定单层递归逻辑

如果不偷当前节点,那么左右孩⼦就可以偷,⾄于到底偷不偷⼀定是选⼀个最⼤的,所以:Math.max(left[0], left[1]) + Math.max(right[0],right[1])

如果是偷当前节点,那么左右孩⼦就不能偷,node.val + left[0] + right[0]

最后当前节点的状态就是dp数组:包含不偷当前节点得到的最⼤⾦钱和偷当前节点得到的最⼤⾦钱

三、代码演示

class Solution {
    public int rob(TreeNode root) {
        int[] res = robTree(root);
        return Math.max(res[0], res[1]);
    }
    
    public int[] robTree(TreeNode node){
        int dp[] = new int[2];
        //终止条件:一个结点都没有,空节点,返回 0,对应后序遍历时候的递归终止条件
        if(node==null){
            return new int[]{0, 0};
        }

        //递归遍历左右子节点
        int[] left = robTree(node.left);
        int[] right = robTree(node.right);

        //不偷node:Max(左孩子不偷,左孩子偷) + Max(右孩子不偷,右孩子偷)
        dp[0] = Math.max(left[0], left[1]) + Math.max(right[0],right[1]);

        //偷node:左孩子不偷+ 右孩子不偷 + 当前节点偷
        dp[1] = node.val +left[0] + right[0];
        return dp;
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值