一、题目描述
- 给定一个二叉树,我们在树的节点上安装摄像头。
- 节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。
- 计算监控树的所有节点所需的最小摄像头数量。
二、理解题意
- 在本题中,每个节点可以监视父节点、自身以及子节点
- 要求在所有节点都被监视到时候所需最小的摄像头的数目
- 摄像头放到非叶子节点上监视的范围最大(父节点+左右子节点)
- 任意一个节点是否放置摄像头取决于其父节点+左子节点+右子节点的状态
三、解法一:暴力搜索
从上往下,依次搜索所有可能性,取最小
- interval=2, 则当前节点必须放置摄像头
- interval=1 + 左右子节点都不存在
(1)此节点必须放置 - interval=1 + 左右子节点都存在
(1)在此节点放置
(2)放置在左子节点
(3)放置在右子节点 - interval=1 + 左右子节点存在一个
(1)在此节点放置
(2)放置在左子节点
(3)放置在右子节点 - interval=0
(1)在此节点放置
(2)放置在左子节点
(3)放置在右子节点
class Solution {
public int minCameraCover(TreeNode root){
// 注意对于root结点, 如interval=0,表示它的父节点放置了摄像头,
// root肯定可以不放,不符题意
return search(root, 1);
}
public int search(TreeNode root, int interval) {// interval表示当前结点(不包含)之上有多少层结点没有放置摄像头
if(root == null){ return 0; }// 空结点无需放置
if(interval == 1 && root.left == null && root.right == null){//当没有孩子节点,父节点未放置摄像头,则需放置
root.val = 1;
return 1;
}
if(interval == 1 && root.left != null && root.right != null){
// 当有1个间隔,两个孩子,1)此节点放置 2)此节点不放,其左节点放 2)此节点不放,其右节点放
root.val = 1;
int x = search(root.left, 0) + search(root.right, 0) + 1;
root.val = 0;//此节点不放置
int y = search(root.left, interval) + search(root.right, interval + 1);// 在右结点放置
int z = search(root.left, interval + 1) + search(root.right, interval);// 在左节点放置
// 对比三种情况 返回最小值
if(x < Math.min(y, z)){
root.val = 1;
return x;
} else {
return Math.min(y, z);
}
}
if(interval == 2){//间隔为2,一点要亮灯
root.val = 1;
return search(root.left, 0) + search(root.right, 0) + 1;
}else {
// 间隔为0 或 间隔为1+左右子树有一个为空
root.val = 1;//放置此节点
int x = search(root.left, 0) + search(root.right, 0) + 1;
root.val = 0;
// 左右子树都放置(只有一个能放成功,因为必有一个为空)
int y = search(root.left, interval + 1) + search(root.right, interval + 1);
if(x < y){
root.val = 1;
return x;
} else {
return y;
}
}
}
}
四、解法二:动态规划
class Solution {
private final int NOT_COVERED = 0;
private final int HAS_CAMERA = 1;
private final int CVERED = 2;
int res = 0;// 全局变量,表示所需要的摄像头的数量
public int minCameraCover(TreeNode root) {
if (root == null){ return 0; }
//如果最后返回的是NOT_COVERED,表示root节点的子节点也没有相机,所以root节点要添加一个相机
if (dfs(root) == NOT_COVERED){ res++; }
return res;
}
public int dfs(TreeNode root) {
//如果是空的,就不需要相机了
if (root == null){ return CVERED; }//
int left = dfs(root.left), right = dfs(root.right);
//如果左右子节点有一个是NOT_COVERED,表示的是子节点既没相机,也没相机覆盖它,所以当前节点需要有一个相机
if (left == NOT_COVERED || right == NOT_COVERED) {
//在当前节点放一个相机,统计相机的个数
res++;
return HAS_CAMERA;
}
//如果左右子节点只要有一个有相机,那么当前节点就不需要相机了,否则返回一个没有相机的标记
return left == HAS_CAMERA || right == HAS_CAMERA ? CVERED : NOT_COVERED;
}
}