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]);
}
};