🌲968.监控二叉树
给定一个二叉树,我们在树的节点上安装摄像头。
节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。
计算监控树的所有节点所需的最小摄像头数量。
示例
示例 :
- 输入:[0,0,null,0,0]
- 输出:1
- 解释:如图所示,一台摄像头足以监控所有节点。
提示:
- 给定树的节点数的范围是
[1, 1000]
。 - 每个节点的值都是 0。
思路
层次遍历
开始思路是层次遍历二叉树,然后看奇数层还是偶数层的节点少,将监控放在节点数少的层上即可,但是忽略了一点,能被监控到的节点不需要再进行监控,也就是隔一层监控一个不是最好的监控方案
所以需要三层一分组,一组中只需要中间的一层安装监控即可,不足三层的分成两种情况,所以一共有三种情况:
level%3=0
,此时只需要统计余3得2的层中有多少节点即可level%3=2
,此时也只需要统计余3得2的层中有多少节点level%3=1
,分开讨论,看奇数层和偶数层那个节点少
所以层次遍历的过程中需要统计层数和每一层的节点数
执行流程
- 执行层次遍历,统计二叉树的层数和每层的节点数
- 判断
level%3
的余数,从而分成两种(1,2可以合并成一种)情况 - 余数为2或者0,直接统计统计余3得2的层中有多少节点
- 余数为1,直接判断奇偶层中那个节点数少
但是这样只能通过一部分,情况模拟不全
后序遍历
基本思想就是孩子节点中有未被覆盖的,那么当前节点就需要装摄像头
如果孩子节点都被覆盖,那么就当前节点的父节点就需要装摄像头
如果孩子节点装了摄像头,那么当前节点就被覆盖
叶子节点不能被装摄像头,因为二叉树越底层节点越多,所以叶子节点不能装摄像头,尽可能地将摄像头向上装
一个节点有摄像头,上下两层都不用装。一个节点只是被覆盖,那么上层就需要被覆盖(自己装摄像头或者父亲装摄像头)
最后单独处理根节点,因为根节点可能自成一组
执行流程
直接进行后序遍历,分成四种情况判断:
- 左右孩子都被覆盖,当前节点的父节点需要装摄像头
- 左右孩子任有一个为被覆盖,此时当前节点需要装摄像头
- 左右孩子任有一个有摄像头,当前节点被覆盖,父节点不用管它
注意2,3的顺序不能反,走到3默认两个孩子都被覆盖,就看是不是自带摄像头的覆盖
如果2,3顺序反过来,可能会出现一个孩子有摄像头,一个孩子不被覆盖,当前节点撒手不管的情况,因为当前节点没有排除任有一个为被覆盖,直接认为自己被覆盖,也就不会装摄像头
代码
层次遍历
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
| class Solution {
public:
int minCameraCover(TreeNode* root) {
if(root==nullptr)
return 0;
if(root->left==nullptr&&root->right==nullptr)
return 1;
queue<TreeNode*> q;//使用一个队列层次遍历二叉树
int level=0;//记录当前的层数
vector<int> count;//统计每一层的节点数
count.push_back(0);//让每层的节点数下标从1开始
q.push(root);
while(!q.empty()){
int size=q.size();
count.push_back(size);//统计一层的节点数
//遍历一层节点
for(int i=0;i<size;++i){
//pop没有返回值,所以需要先front再pop
TreeNode *node=q.front();
q.pop();
if(node->left!=nullptr){
q.push(node->left);
}
if(node->right!=nullptr){
q.push(node->right);
}
}
++level;
}
//遍历完二叉树之后,分情况讨论
int res=0;
int num=0;
if(level%3==0||level%3==2){//看余3得2的层中有多少节点
for(int i=1;i<count.size();++i){
if(i%3==2){
res+=count[i];
}
}
}
if(level%3==1){
for(int i=1;i<count.size();++i){
if(i%2==0){
res+=count[i];
}
num+=count[i];
}
//判断奇偶层那个节点数少
res=res<(num-res)?res:(num-res);
}
return res;
}
};
|
后序遍历
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| class Solution {
private:
int result;//全局变量统计监控数量
int traversal(TreeNode* cur) {
//为了让叶子节点不装摄像头,叶子节点的孩子节点默认被覆盖
if (cur == NULL) return 2;
int left = traversal(cur->left); // 左
int right = traversal(cur->right); // 右
//左右孩子都被覆盖,当前节点无法覆盖,其父节点装摄像头
if (left == 2 && right == 2) return 0;
//左右孩子任有一个无法被覆盖,此时当前阶段需要装摄像头
if (left == 0 || right == 0) {
result++;
return 1;
}
//到这里就是左右孩子都被覆盖,就看是有摄像头的覆盖还是无摄像头的覆盖
if (left == 1 || right == 1) return 2;
}
public:
int minCameraCover(TreeNode* root) {
result = 0;
// 最后处理根节点
if (traversal(root) == 0) { // root 无覆盖
result++;
}
return result;
}
};
|
总结
层序遍历模拟过程中总会丢掉一些情况,而题目的基本思想就是孩子节点中任有一个未被监控覆盖,当前节点就需要装摄像头,叶子节点属于孩子节点,所以不装摄像头,从下向上遍历模拟器来更方便,所以选择后序遍历,分为三种情况:
- 两个孩子节点都被监控覆盖,当前节点不用管他们,只需让自己的父节点装监控覆盖到自己即可
- 两个孩子节点任有一个未被覆盖,此时当前节点需要装监控才能覆盖到他们
- 两个孩子任有一个有监控,当前节点
node
可以被覆盖,node
的父节点不用管它,只需管好自己
2,3的顺序不能调换