监控二叉树
难度:
标签:
题目描述
给定一个二叉树,我们在树的节点上安装摄像头。
节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。
计算监控树的所有节点所需的最小摄像头数量。
示例 1:
输入:[0,0,null,0,0] 输出:1 解释:如图所示,一台摄像头足以监控所有节点。
示例 2:
输入:[0,0,null,0,null,0,null,null,0] 输出:2 解释:需要至少两个摄像头来监视树的所有节点。 上图显示了摄像头放置的有效位置之一。
提示:
- 给定树的节点数的范围是
[1, 1000]
。 - 每个节点的值都是 0。
代码结果
运行时间: 24 ms, 内存: 16.3 MB
/*
题目思路:
- 与 Java 实现类似,但我们将尝试使用 Java Streams 进行处理。
- 然而,由于树的节点操作通常涉及递归或队列的逐层处理,Java Streams 可能不适用。
- 因此,本方案仍然会使用递归实现 DFS。
*/
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
public class Solution {
private int cameras = 0;
public int minCameraCover(TreeNode root) {
if (dfs(root) == 0) cameras++;
return cameras;
}
private int dfs(TreeNode node) {
if (node == null) return 2;
int left = dfs(node.left);
int right = dfs(node.right);
if (left == 0 || right == 0) {
cameras++;
return 1;
}
return (left == 1 || right == 1) ? 2 : 0;
}
}
解释
方法:
题解采用了递归的方法,利用后序遍历(先处理子节点,再处理当前节点)的方式来决定在哪些节点上安装摄像头。递归函数返回三种状态:0 表示当前节点未被覆盖,1 表示当前节点已安装摄像头,2 表示当前节点已被覆盖但未安装摄像头。根据子节点的状态来决定当前节点的操作:
1. 如果任一子节点未被覆盖(返回0),则在当前节点安装摄像头,并返回状态1。
2. 如果所有子节点都已被覆盖(返回2),则当前节点返回状态0。
3. 如果任一子节点安装了摄像头(返回1),则当前节点返回状态2,表示已被覆盖。
最后,若根节点在递归后仍未被覆盖,则在根节点额外安装一个摄像头。
时间复杂度:
O(n)
空间复杂度:
O(n) in the worst case, O(log(n)) in the best case
代码细节讲解
🦆
在递归函数中,如何判断一个节点是否已经被覆盖?
▷🦆
为什么最后还需要检查根节点是否被覆盖,根据递归逻辑,根节点不是应该总是被覆盖吗?
▷🦆
递归终止条件中,为什么不存在的节点(cur为None)被认为是已经覆盖(返回状态2)?
▷🦆
请解释为什么当左右子节点均返回状态2时,当前节点的状态返回0(未被覆盖)?
▷相关问题
在二叉树中分配硬币
给你一个有 n
个结点的二叉树的根结点 root
,其中树中每个结点 node
都对应有 node.val
枚硬币。整棵树上一共有 n
枚硬币。
在一次移动中,我们可以选择两个相邻的结点,然后将一枚硬币从其中一个结点移动到另一个结点。移动可以是从父结点到子结点,或者从子结点移动到父结点。
返回使每个结点上 只有 一枚硬币所需的 最少 移动次数。
示例 1:

输入:root = [3,0,0] 输出:2 解释:一枚硬币从根结点移动到左子结点,一枚硬币从根结点移动到右子结点。
示例 2:

输入:root = [0,3,0] 输出:3 解释:将两枚硬币从根结点的左子结点移动到根结点(两次移动)。然后,将一枚硬币从根结点移动到右子结点。
提示:
- 树中节点的数目为
n
1 <= n <= 100
0 <= Node.val <= n
- 所有
Node.val
的值之和是n