一、题目描述
在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。
计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。
提示:
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;
}
}