题目链接
题目描述
- 小偷进入一片区域准备再一次行窃,这片区域仅有一个入口,称为root。在root入口旁边,每个房屋有且仅有一个“父房屋”。聪明的小偷发现这片区域内所有的房屋形成了一棵二叉树。另外,如果同一个晚上,两间直接相连的房屋被闯入的话,自动报警器就会启动。现已知每个房屋里面的金额,要求计算出小偷在不惊动警察的情况下,今晚最多可以偷到多少钱。
解题思路
- 抽象一下题目要求,其实本题要计算的是二叉树某些结点的和的最大值,这些结点需要满足的条件是两两不直接相连。
- 简单分析可以得出,对于任意一个二叉树结点,可以分两种情况考虑:
- 它是被选中的结点,那么它的左右子结点都不是被选中的结点。
- 它不是被选中的结点,这种场景可再细分:
- 左子结点被选中,右子结点也被选中。
- 左子结点被选中,右子结点不被选中。
- 左子结点不被选中,右子结点被选中。
- 左子结点不被选中,右子结点也不被选中。
- 左右子结点的情况,可以递归处理。
- 从根结点出发,对于每个结点根据上述分析进行遍历,即可得出结果。理论上,每个结点有两种状态(被选中与不被选中),若结点数为n,那么时间复杂度为O(2^n),但是对于父结点被选中的情况,子结点很多场景其实不需要考虑(称为剪枝),最终时间复杂度远不及O(2^n)。另外需要考虑结点值为负的情况,每个结点在遍历时,应该取0作为初始值。
C++代码
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int rob(TreeNode* root) {
int max_sum = 0;
// 根结点为空,直接返回0
if (!root) {
return max_sum;
}
// 根结点被选中的情况
int tmp = travel(root, true) + root->val;
if (tmp > max_sum) {
max_sum = tmp;
}
// 根结点未被选中的情况
tmp = travel(root, false);
if (tmp > max_sum) {
max_sum = tmp;
}
return max_sum;
}
// 遍历某个结点,获取它的左右子结点各种情况的和的最大值
int travel(TreeNode* node, bool cur_state) {
int tmp;
// 计算左结点各种情况的最大值
int max_left = 0;
if (node->left) {
// 不选中左子结点的情况
tmp = travel(node->left, false);
if (tmp > max_left) {
max_left = tmp;
}
// 若当前结点未被选中,则考虑选中左子结点的情况
if (!cur_state) {
tmp = travel(node->left, true) + node->left->val;
if (tmp > max_left) {
max_left = tmp;
}
}
}
// 计算右结点各种情况的最大值
int max_right = 0;
if (node->right) {
// 不选中右子结点的情况
tmp = travel(node->right, false);
if (tmp > max_right) {
max_right = tmp;
}
// 若当前结点未被选中,则考虑选中右子结点的情况
if (!cur_state) {
tmp = travel(node->right, true) + node->right->val;
if (tmp > max_right) {
max_right = tmp;
}
}
}
// 返回左右子结点最大值之和
return max_left + max_right;
}
};