【问题描述】[中等]

【解答思路】

1. 动态规划

第 1 步:状态定义
dp[node][j] :这里 node 表示一个结点,以 node 为根结点的树,并且规定了 node 是否偷取能够获得的最大价值。

j = 0 表示 node 结点不偷取;
j = 1 表示 node 结点偷取。
第 2 步: 推导状态转移方程
根据当前结点偷或者不偷,就决定了需要从哪些子结点里的对应的状态转移过来。

如果当前结点不偷,左右子结点偷或者不偷都行,选最大者;
如果当前结点偷,左右子结点均不能偷。
(状态转移方程的表述有点复杂,请大家直接看代码。)

第 3 步: 初始化
一个结点都没有,空节点,返回 0,对应后序遍历时候的递归终止条件;

第 4 步: 输出
在根结点的时候,返回两个状态的较大者。

第 5 步: 思考优化空间
优化不了。
时间复杂度:O(N) 空间复杂度:O(N)

public class Solution {// 树的后序遍历public int rob(TreeNode root) {int[] res = dfs(root);return Math.max(res[0], res[1]);}private int[] dfs(TreeNode node) {if (node == null) {return new int[]{0, 0};}// 分类讨论的标准是:当前结点偷或者不偷// 由于需要后序遍历,所以先计算左右子结点,然后计算当前结点的状态值int[] left = dfs(node.left);int[] right = dfs(node.right);// dp[0]:以当前 node 为根结点的子树能够偷取的最大价值,规定 node 结点不偷// dp[1]:以当前 node 为根结点的子树能够偷取的最大价值,规定 node 结点偷int[] dp = new int[2];dp[0] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);dp[1] = node.val + left[0] + right[0];return dp;}
}
2. 递归

使用爷爷、两个孩子、4 个孙子来说明问题
首先来定义这个问题的状态
爷爷节点获取到最大的偷取的钱数呢

  1. 首先要明确相邻的节点不能偷,也就是爷爷选择偷,儿子就不能偷了,但是孙子可以偷
  2. 二叉树只有左右两个孩子,一个爷爷最多 2 个儿子,4 个孙子
    根据以上条件,我们可以得出单个节点的钱该怎么算
    4 个孙子偷的钱 + 爷爷的钱 VS 两个儿子偷的钱 哪个组合钱多,就当做当前节点能偷的最大钱数。这就是动态规划里面的最优子结构
    由于是二叉树,这里可以选择计算所有子节点

4 个孙子投的钱加上爷爷的钱如下
int method1 = root.val + rob(root.left.left) + rob(root.left.right) + rob(root.right.left) + rob(root.right.right)
两个儿子偷的钱如下
int method2 = rob(root.left) + rob(root.right);
挑选一个钱数多的方案则
int result = Math.max(method1, method2);

2.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.2 记忆化递归
针对2.1中速度太慢的问题,经过分析其实现,我们发现爷爷在计算自己能偷多少钱的时候,同时计算了 4 个孙子能偷多少钱,也计算了 2 个儿子能偷多少钱。这样在儿子当爷爷时,就会产生重复计算一遍孙子节点。

于是乎我们发现了一个动态规划的关键优化点

我们这一步针对重复子问题进行优化,我们在做斐波那契数列时,使用的优化方案是记忆化,但是之前的问题都是使用数组解决的,把每次计算的结果都存起来,下次如果再来计算,就从缓存中取,不再计算了,这样就保证每个数字只计算一次。
由于二叉树不适合拿数组当缓存,我们这次使用哈希表来存储结果,TreeNode 当做 key,能偷的钱当做 value

public int rob(TreeNode root) {//hashmap 记忆化HashMap<TreeNode, Integer> memo = new HashMap<>();return robInternal(root, memo);
}public int robInternal(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 += (robInternal(root.left.left, memo) + robInternal(root.left.right, memo));}if (root.right != null) {money += (robInternal(root.right.left, memo) + robInternal(root.right.right, memo));}int result = Math.max(money, robInternal(root.left, memo) + robInternal(root.right, memo));///!!!memo.put(root, result);return result;
}

【总结】

1. 动态规划流程

第 1 步:设计状态
第 2 步:状态转移方程
第 3 步:考虑初始化
第 4 步:考虑输出
第 5 步:考虑是否可以状态压缩

2.递归 递推公式要找准 后用记忆化搜索优化 这题不能用BFS,层次遍历不符合题解

转载链接:https://leetcode-cn.com/problems/house-robber-iii/solution/san-chong-fang-fa-jie-jue-shu-xing-dong-tai-gui-hu/
转载链接:https://leetcode-cn.com/problems/house-robber-iii/solution/shu-xing-dp-ru-men-wen-ti-by-liweiwei1419/

[Leetcode][第337题][JAVA][打家劫舍3][递归][动态规划]相关推荐

  1. [Leetcode][第112题][JAVA][路径总和][递归][队列]

    [问题描述][中等] [解答思路] 1. 递归 时间复杂度:O(N) 空间复杂度:O(H) 从根节点开始,每当遇到一个节点的时候,从目标值里扣除节点值,一直到叶子节点判断目标值是不是被扣完. clas ...

  2. [Leetcode][第174题][JAVA][地下城游戏][DFS][动态规划]

    [问题描述][中等] [解答思路] 1. 回溯(暴力)& 优化 超时,需要优化 public int calculateMinimumHP(int[][] dungeon) {if (dung ...

  3. [Leetcode][第647题][JAVA][回文子串][动态规划][中心扩展][Manacher 算法]

    [问题描述][中等] [解答思路] 1. 暴力 首先明确如何判断一个字符串是否为回文字符串.第一个字符与最后一个字符相同,第二个字符与倒数第二个字符相同-关于中心位置轴对称. 本题要求一共有多少个回文 ...

  4. [Leetcode][第97题][JAVA][交错字符串][BFS][动态规划]

    [问题描述][中等] [解答思路] 1. 动态规划 第 1 步:设计状态 f(i,j) 表示 s 1的前 i个元素和 s2 的前 j个元素是否能交错组成 s3的前 i + j 个元素 第 2 步:状态 ...

  5. [Leetcode][第63题][JAVA][不同路径2][动态规划][压缩路径]

    [问题描述][中等] [解答思路] 1. 动态规划流程 第 1 步:设计状态 第 2 步:状态转移方程 第 3 步:考虑初始化 第一行第一列 没有遇到"障碍" 就为1 如果u(i, ...

  6. [Leetcode][第44题][JAVA][通配符匹配][贪心][动态规划]

    [问题描述][困难] [解答思路] 1. 动态规划 第 1 步:设计状态 dp[i][j]dp[i][j] 表示字符串 ss 的前 ii 个字符和模式 pp 的前 jj 个字符是否能匹配 第 2 步: ...

  7. [Leetcode][第889题][JAVA][根据前序和后序遍历构造二叉树][分治][递归]

    [问题描述][中等] [解答思路] copyOfRange class Solution {public TreeNode constructFromPrePost(int[] pre, int[] ...

  8. [Leetcode][第106题][JAVA][ 从中序与后序遍历序列构造二叉树][分治][递归]

    [问题描述][中等] [解答思路] public class Solution {public TreeNode buildTree(int[] inorder, int[] postorder) { ...

  9. [Leetcode][第111题][JAVA][BFS][二叉树的最小深度][BFS][递归]

    [问题描述][简单] [解答思路] 1. 递归 自下而上 基本情况/结束条件 : 叶子节点的定义是左孩子和右孩子都为 null 时叫做叶子节点 当 root 节点左右孩子都为空时,返回 1 当 roo ...

最新文章

  1. Linux下开发优秀链接
  2. (转载)虚幻引擎3--9掌握虚幻技术UnrealScript 预处理器
  3. 三、css 和 js 的装载与执行
  4. 关于datediff(year,开始日期,结束日期)中year格式的说明,特别注意year两边不能加引号,否则报错...
  5. Taro+react开发(36)每一个节点要一个view包裹
  6. Nagios 安装教程
  7. Java 蓝桥杯 字母图形
  8. android mtk平台的fm停止搜索,【MTK平台,手机工程模式知识及方法详解】
  9. extjs combobox下拉框显示位置问题
  10. ActiveMQ下载与安装(Linux环境下进行)
  11. unity3d模型制作规范
  12. 智能安防及视频监控系统
  13. arcgis属性字段fid修改 修改出现bad value
  14. 识图在线识图_以图搜图在线搜索软件
  15. 旭辉完成2020年销售目标:还要加码广西,却在北京违规被通报
  16. JMeter参数大小写转换
  17. Unbuntu 安装 TeamViewer14
  18. iOS开发实用技术之MapKit框架的使用
  19. perl(Time::HiRes) is needed mysql5.7
  20. Ruoyi 图片上传

热门文章

  1. l2-006 树的遍历
  2. IT基础架构规划方案二(计算机系统与机房规划规划)
  3. 早晨爬山,三餐自备——程序员也可以这样生活、工作
  4. xcode中工程引用设置
  5. 服务器虚拟多台linux,VirtualBox环境下基于多台Ubuntu虚拟机的Hadoop分布式计算环境搭建...
  6. android 跳转页面出错,Android 页面跳转(无/含有返回结果)
  7. linux vue node占用内存过大,vue 大型应用内存泄漏改造经验
  8. [官方] mysql 性能优化文档(中英文自译)
  9. Vmware centos无法连接网络
  10. oracle中如何设置主键并且让其自动增长