《代码随想录》Ⅷ 贪心算法 968. 监控二叉树
革命尚未成功,同志仍需努力!
题目:力扣链接
-
给定一个二叉树,我们在树的节点上安装摄像头。
节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。
计算监控树的所有节点所需的最小摄像头数量。
一、思想
这道题的核心是使用贪心算法+后序遍历来确定摄像头的最小安装位置。具体思路如下:
-
状态定义:
- 0:该节点无覆盖
- 1:该节点有摄像头
- 2:该节点有覆盖(但无摄像头)
-
后序遍历策略:
- 从叶子节点开始向上处理
- 优先让叶子节点的父节点安装摄像头(贪心思想)
-
状态转移规则:
- 左右子节点都有覆盖时,当前节点不安装(状态0)
- 任一子节点无覆盖时,当前节点必须安装(状态1)
- 任一子节点有摄像头时,当前节点获得覆盖(状态2)
二、代码
class Solution
{
public:
int result;
int traversal(TreeNode *root)
{
if (root == nullptr)
{
return 2; // 空节点,该节点有覆盖
}
int left = traversal(root->left); // 左子树的状态
int right = traversal(root->right); // 右子树的状态
// 情况1:左右子树都有覆盖
if (left == 2 && right == 2)
{
return 0; // 本节点无覆盖
}
// 情况2:左右子树至少有一个无覆盖
else if (left == 0 || right == 0)
{
result++; // 本节点需要放置摄像头
return 1; // 本节点需要放置摄像头
}
// 情况4:左右子树都有覆盖
else if (left == 1 || right == 1)
{
return 2; // 本节点有覆盖
}
return -1; // 其他情况,返回-1,这是为了防止代码逻辑错误
}
int minCameraCover(TreeNode *root)
{
result = 0; // 初始化摄像头数量
if (traversal(root) == 0)
{
result++; // 根节点无覆盖,需要放置摄像头
}
return result;
}
};
三、代码解析
1. 算法工作原理分解
1.1 后序遍历阶段
- 遍历顺序:左右根
- 关键操作:递归处理左右子树
- 状态获取:
left = traversal(root->left),right = traversal(root->right)
1.2 状态判断阶段
-
情况1:左右子树都有覆盖(状态2)
- 当前节点不安装(返回0)
-
情况2:任一子树无覆盖(状态0)
- 当前节点必须安装(result++)
- 返回状态1
-
情况3:任一子树有摄像头(状态1)
- 当前节点获得覆盖(返回2)
1.3 根节点特殊处理
- 最终检查:遍历结束后检查根节点状态
- 处理逻辑:若根节点无覆盖(状态0),则必须安装摄像头
2. 关键点说明
- 贪心策略:在保证覆盖的前提下,尽量在最底层节点安装摄像头
- 状态设计:三种状态确保无遗漏情况
- 空节点处理:视为已覆盖(状态2),避免影响决策
- 结果更新:只在安装摄像头时增加计数器
四、复杂度分析
-
时间复杂度:O(n)
- 需要遍历二叉树的所有节点
- 每个节点仅处理一次
- 典型的后序遍历时间复杂度
-
空间复杂度:O(h)
- 递归栈空间取决于树的高度
- 最坏情况下(链状树)为O(n)
- 平均情况下(平衡树)为O(log n)
白展堂:人生就是这样,苦和累你总得选一样吧?哪有什么好事都让你一个人占了呢。 ——《武林外传》
136

被折叠的 条评论
为什么被折叠?



