Hard
题目描述
给定一个二叉树的根节点 root,我们在树的节点上安装摄像头。节点上的每个摄像头都可以监视其父对象、自身及其直接子对象。
计算监控树的所有节点所需的最少摄像头数量。
示例 1:
输入:root = [0,0,null,0,0]
输出:1
解释:如图所示,一个摄像头就足以监控所有节点。
示例 2:
输入:root = [0,0,null,0,null,0,null,null,0]
输出:2
解释:需要至少两个摄像头来监视树的所有节点。上图显示了摄像头放置的有效配置之一。
提示:
- 树中节点的数量在
[1, 1000]范围内 Node.val == 0
解题思路
这是一个树形 DP 问题,关键是要确定每个节点的状态以及状态转移关系。
我们可以为每个节点定义三种状态:
- 状态 0:该节点未被覆盖,需要父节点放置摄像头
- 状态 1:该节点被覆盖,但自身没有摄像头
- 状态 2:该节点放置了摄像头
采用后序遍历,从叶子节点开始向上计算:
状态转移规则:
- 对于叶子节点:返回状态 0(未被覆盖)
- 对于内部节点:
- 如果任一子节点未被覆盖(状态 0),当前节点必须放摄像头(状态 2)
- 如果任一子节点有摄像头(状态 2),当前节点被覆盖(状态 1)
- 如果所有子节点都被覆盖但无摄像头(状态 1),当前节点未被覆盖(状态 0)
特殊处理: 如果根节点最终状态为 0(未被覆盖),需要额外在根节点放置一个摄像头。
这种贪心策略能保证摄像头数量最少,因为我们优先让叶子节点的父节点放置摄像头,这样能覆盖更多节点。
代码实现
class Solution {
public:
int cameras = 0;
int minCameraCover(TreeNode* root) {
cameras = 0;
int rootState = dfs(root);
// 如果根节点未被覆盖,需要在根节点放摄像头
if (rootState == 0) cameras++;
return cameras;
}
private:
// 返回值:0-未被覆盖,1-被覆盖但无摄像头,2-有摄像头
int dfs(TreeNode* node) {
if (!node) return 1; // 空节点视为被覆盖
int left = dfs(node->left);
int right = dfs(node->right);
// 如果任一子节点未被覆盖,当前节点必须放摄像头
if (left == 0 || right == 0) {
cameras++;
return 2;
}
// 如果任一子节点有摄像头,当前节点被覆盖
if (left == 2 || right == 2) {
return 1;
}
// 所有子节点都被覆盖但无摄像头,当前节点未被覆盖
return 0;
}
};
class Solution:
def minCameraCover(self, root: Optional[TreeNode]) -> int:
self.cameras = 0
def dfs(node):
if not node:
return 1 # 空节点视为被覆盖
left = dfs(node.left)
right = dfs(node.right)
# 如果任一子节点未被覆盖,当前节点必须放摄像头
if left == 0 or right == 0:
self.cameras += 1
return 2
# 如果任一子节点有摄像头,当前节点被覆盖
if left == 2 or right == 2:
return 1
# 所有子节点都被覆盖但无摄像头,当前节点未被覆盖
return 0
root_state = dfs(root)
# 如果根节点未被覆盖,需要在根节点放摄像头
if root_state == 0:
self.cameras += 1
return self.cameras
public class Solution {
private int cameras = 0;
public int MinCameraCover(TreeNode root) {
cameras = 0;
int rootState = Dfs(root);
// 如果根节点未被覆盖,需要在根节点放摄像头
if (rootState == 0) cameras++;
return cameras;
}
private int Dfs(TreeNode node) {
if (node == null) return 1; // 空节点视为被覆盖
int left = Dfs(node.left);
int right = Dfs(node.right);
// 如果任一子节点未被覆盖,当前节点必须放摄像头
if (left == 0 || right == 0) {
cameras++;
return 2;
}
// 如果任一子节点有摄像头,当前节点被覆盖
if (left == 2 || right == 2) {
return 1;
}
// 所有子节点都被覆盖但无摄像头,当前节点未被覆盖
return 0;
}
}
var minCameraCover = function(root) {
let cameras = 0;
function dfs(node) {
if (!node) return 2;
let left = dfs(node.left);
let right = dfs(node.right);
if (left === 0 || right === 0) {
cameras++;
return 1;
}
if (left === 1 || right === 1) {
return 2;
}
return 0;
}
return dfs(root) === 0 ? cameras + 1 : cameras;
};
复杂度分析
| 复杂度 | 分析 |
|---|---|
| 时间复杂度 | O(n),其中 n 是树中节点的数量,每个节点只访问一次 |
| 空间复杂度 | O(h),其中 h 是树的高度,递归调用栈的深度 |