【中等】【337】打家劫舍III【树形DP】
链接点我
题目描述
思路
1.暴力递归-最优子结构
public int rob(TreeNode root) {
if(root == null){
return 0;
}
int money = root.val;
if(root.left != null){
money += rob(root.left.left) + rob(root.left.right);
}
if(root.right != null){
money += rob(root.right.left) + rob(root.right.right);
}
return Math.max(money,rob(root.left)+rob(root.right));
}
2. 记忆化-解决重复子问题
针对解法一种速度太慢的问题,经过分析其实现,我们发现爷爷在计算自己能偷多少钱的时候,同时计算了4个孙子能偷多少钱,也计算了2个儿子能偷多少钱。这样在儿子当爷爷时,就会产生重复计算一遍孙子节点。
于是乎我们发现了一个动态规划的关键优化点
重复子问题
我们这一步针对重复子问题进行优化,我们在做斐波那契数列时,使用的优化方案是记忆化,但是之前的问题都是使用数组解决的,把每次计算的结果都存起来,下次如果再来计算,就从缓存中取,不再计算了,这样就保证每个数字只计算一次。
由于二叉树不适合拿数组当缓存,我们这次使用哈希表来存储结果,TreeNode当做key,能偷的钱当做value
public int rob(TreeNode root) {
if(root == null){
return 0;
}
HashMap<TreeNode,Integer> memo = new HashMap<>();
return robCore(root,memo);
}
private int robCore(TreeNode root,HashMap<TreeNode,Integer> memo){
if(root == null){return 0;}
if(memo.containsKey(root)){
return memo.get(root);
}
int money = root.val;
if(root.left != null){
money += robCore(root.left.left,memo)+robCore(root.left.right,memo);
}
if(root.right != null){
money += robCore(root.right.left,memo)+robCore(root.right.right,memo);
}
int result = Math.max(money,robCore(root.left,memo)+robCore(root.right,memo));
memo.put(root,result);
return result;
}
3.终极解法
public int rob(TreeNode root) {
if(root == null){
return 0;
}
int[] result = robCore(root);
return Math.max(result[0],result[1]);
}
public int[] robCore(TreeNode root){
if(root == null){
return new int[2];
}
int[] result = new int[2];
int[] left = robCore(root.left);
int[] right = robCore(root.right);
result[0] = Math.max(left[0],left[1])+ Math.max(right[0],right[1]);
result[1] =left[0] + right[0] + root.val;
return result;
}