337. 打家劫舍 III 树形DP 与二叉树后续遍历理解

题目描述

在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。

计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。

示例 1:

输入: [3,2,3,null,3,null,1]

 3
/ \

2 3
\ \
3 1

输出: 7
解释: 小偷一晚能够盗取的最高金额 = 3 + 3 + 1 = 7.
示例 2:

输入: [3,4,5,1,3,null,1]

3
/
4 5
/ \ \
1 3 1

输出: 9
解释: 小偷一晚能够盗取的最高金额 = 4 + 5 = 9.

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/house-robber-iii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


  • 采用纯粹后续遍历递归方式 ,LeetCode居然没有提示超时
class Solution {
public:
    int rob(TreeNode* root) {
     //采用纯粹后续遍历递归方式
    //  设rob(node)为当前节点及以下考虑范围内可以得到的最大金额 *二叉树核心
    //  偷当前节点:
    //   res1= node.val + rob(node.left.left) + rob(node.left.right)  +rob(node.right.left) + rob(node.right.right)  ;
    //  不偷当前节点:
    //  res2= rob(node.left) + rob(node.right)
    //  return max(res1,res2)
    //递归终止条件
      if(!root) return 0;
      int res1 = root->val;
      if(root->left) res1+= rob(root->left->left )+ rob(root->left->right);
       if(root->right) res1+= rob(root->right->left )+ rob(root->right->right);
       //递归逻辑
       int res2= rob(root->left)+ rob(root->right);
       return std::max(res1,res2);
    }
};
  • 采用记忆化递归后,速度明显提升

class Solution {
public:
  std::unordered_map< TreeNode* , int> tmap;
    int rob(TreeNode* root) {
     //采用纯粹后续遍历递归方式
    //  设rob(node)为当前节点及以下考虑范围内可以得到的最大金额 *二叉树核心
    //  偷当前节点:
    //   res1= node.val + rob(node.left.left) + rob(node.left.right)  +rob(node.right.left) + rob(node.right.right)  ;
    //  不偷当前节点:
    //  res2= rob(node.left) + rob(node.right)
    //  return max(res1,res2)

    //* 可以看出直接方式存在对节点重复访问的问题
    //  考虑采用记忆化递归
    //递归终止条件
      if(!root) return 0;
      if(tmap.count(root)) return tmap[root];
      int res1 = root->val;
      if(root->left) res1+= rob(root->left->left )+ rob(root->left->right);
       if(root->right) res1+= rob(root->right->left )+ rob(root->right->right);
       //递归逻辑
       int res2= rob(root->left)+ rob(root->right);
         
      
       int res= std::max(res1,res2);
       tmap.emplace(root,res);
       return res;

    }
};
  • 采用树形动态规划

class Solution {
public:

     std::vector<int> robTree(TreeNode * root)
     {
        //递归终止条件
           if(!root)
           {
               std::cout<< "NULL {0,0}"<<std::endl;
               return std::vector<int>{0,0};
           } 
           auto leftVec= robTree(root->left);
           auto rightVec= robTree(root->right);
            //DP递推逻辑
            int No= std::max(leftVec[0], leftVec[1]) +  std::max(rightVec[0], rightVec[1]);
            int Yes= root->val + leftVec[0]+ rightVec[0];
             std::cout<< root->val << "{" << No <<", "<<Yes<< "}" << std::endl;
            return std::vector<int> { No,Yes};

           
     }

    int rob(TreeNode* root) {
//树形DP +  后续遍历递归  
    // retDP={不偷当前点能得到的最大金额, 偷当前点能得到的最大金额}
     
    //retDP[1] = 偷当前节点: node.val + node.left[0],node.right[0]
     //retDP[0]= 不偷当前节点 max(node.left[0],node.left[1]) + max(node.right[0], node.right[1]) 

     //举例验证
    //    3<6,7> 
    //     / \
    //  2<3,2>   3<1,3>
    //     \     \ 
    //    3<0,3>   1<0,1>
    auto X=robTree(root);
    return std::max(X[0],X[1]);
    }
};
posted @ 2021-02-24 14:20  boyang987  阅读(87)  评论(0编辑  收藏  举报