• leetCode 337. 打家劫舍 III 动态规划 房子都连成树了,偷不偷呢? “树形dp“ (递归三部曲 + 动规五部曲)


    小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root 。除了 root 之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果 两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。给定二叉树的 root 。返回 在不触动警报的情况下 ,小偷能够盗取的最高金额 。

    >>思路和分析

    • 可以对一个节点 “偷” 与 “不偷” 得到的最大金钱 做记录,实时计算
    • 动态规划就是使用状态转移容器来记录状态的变化,使用一个长度为2的数组,记录当前的节点 “偷” 与 “不偷” 所得到的最大金钱
    • 在树上进行状态转移,利用递归三部曲 + 动规五部曲 

    >>递归三部曲 + 动规五部曲

    1.确定递归函数的 参数 和 返回值

    返回值是一个长度为2的dp数组,用来记录节点 “偷” 与 “不偷” 得到的金钱

    1. vector<int> robTree(TreeNode* cur) {
    2. ...
    3. }
    • dp[0] 记录 “不偷” 该节点所得到的最大金钱
    • dp[1] 记录 “偷” 该节点所得到的最大金钱

    常见疑惑(O_O)?长度为2的dp数组怎么标记树中的每个节点的状态呢?

    【解惑】其实在递归的过程中,系统栈会保存每一层递归的参数~

    2.确定终止条件

    在遍历过程中,若是遇到空节点,无论 “偷” 还是 “不偷” 都是 0 ,所以直接返回 {0,0}。因为空节点的金钱收益为0。而这也相当于 dp 数组的初始化

    if(cur == NULL) return vector<int>{0,0};

    3.确定遍历顺序

    • 明确使用后序遍历,因为需要通过递归函数的返回值来做下一步计算
    • 通过递归左节点,可得到左节点 “偷” 和 “不偷” 的金钱
    • 通过递归右节点,可得到右节点 “偷” 和 “不偷” 的金钱

    4.确定单层递归的逻辑

    • “偷” 当前节点,那么左右孩子就 “不能偷” val1 = cur -> val + left[0] + right[0];
    • “不偷” 当前节点,那么左右孩子就 “可以偷”在左右孩子的 “偷” 和 “不偷” 记录中选一个最大的,即 val2 = max(left[0],left[1]) + max(right[0],right[1]);
    • 最后当前节点的状态就是 {val2,val1}; (意味着 “不偷” 当前节点得到的最大金钱,“偷” 当前节点得到的最大金钱)

    5.举例推导 dp 数组

    最后max{result[0],result[1]}; 的返回值就是头结点偷得的最大金钱

    1. class Solution {
    2. public:
    3. // 长度为2的数组,0:不偷,1:偷
    4. vector<int> robTree(TreeNode* cur) {
    5. if(cur == NULL) return vector<int>{0,0};
    6. vector<int> left = robTree(cur->left);
    7. vector<int> right = robTree(cur->right);
    8. // 偷 cur,那么就不能偷左右节点
    9. int val1 = cur->val + left[0] + right[0];
    10. // 不偷 cur,那么可以偷也可以不偷左右节点,则取较大的情况
    11. int val2 = max(left[0],left[1]) + max(right[0],right[1]);
    12. // cout << "{val2 , val1} :" << val2 << "," << val1 <
    13. return {val2,val1};
    14. }
    15. int rob(TreeNode* root) {
    16. vector<int> result = robTree(root);
    17. return max(result[0],result[1]);
    18. }
    19. };
    • 时间复杂度:O(n),每个节点只遍历了一次
    • 空间复杂度:O(log n),算上递推系统栈的空间

    参考和推荐文章、视频

    代码随想录 (programmercarl.com)

    动态规划,房间连成树了,偷不偷呢?| LeetCode:337.打家劫舍3_哔哩哔哩_bilibili

    来自代码随想录的课堂截图:

  • 相关阅读:
    powershell批量修改后缀名
    Hadoop源码解析之Mapper数量计算公式
    k8s-20 hpa控制器
    019-第三代软件开发-Git提交规范
    Day 52 单调栈 part01
    级联选择器的二维数组键值问题
    LeetCode C++ 28.实现strStr()
    网站排名下降的原因和解决方法(SEO优化失误可能导致网站排名下降)
    从0开始学统计-什么是回归?
    在 RISC-V 设计中发现可远程利用的漏洞
  • 原文地址:https://blog.csdn.net/weixin_41987016/article/details/133415190