• 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

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

  • 相关阅读:
    GIS之深度学习10:运行Faster RCNN算法
    复习SGI STL二级空间配置器(内存池) | 笔记自用
    k8s--基础--22.11--storageclass--类型--Azure 文件
    计算机网络 | 物理层
    我的 React 最佳实践
    matlab中narginchk函数用法及其举例
    kafka的Java客户端-offset
    vue表格不显示列号123456
    高并发系统 - 接口幂等技术方案,高可用系统架构与技术选型
    Hololens开发-手势拖拽、旋转物体实现
  • 原文地址:https://blog.csdn.net/weixin_41987016/article/details/133415190