【一天一大 lee】 监控二叉树 (难度:困难)-Day20200922
时间:2022-07-25
本文章向大家介绍【一天一大 lee】 监控二叉树 (难度:困难)-Day20200922,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
给定一个二叉树,我们在树的节点上安装摄像头。
节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。
计算监控树的所有节点所需的最小摄像头数量。
示例:
- 示例 1:
示例1
输入:[0,0,null,0,0]
输出:1
解释:如图所示,一台摄像头足以监控所有节点。
- 示例 2:
示例2
输入:[0,0,null,0,null,0,null,null,0]
输出:2
解释:需要至少两个摄像头来监视树的所有节点。 上图显示了摄像头放置的有效位置之一。
提示:
- 给定树的节点数的范围是 [1, 1000]。
- 每个节点的值都是 0。
抛砖引玉
抛砖引玉
遍历二叉树安装摄像头(X),且可被该摄像头监控到的节点标记(Y),未受该节点和其他监控节点监控的节点标记(Z)
递归遍历二叉树:
- 参数二叉树子树;
- 递归终止,遇到叶子节点或者遇到的子树状态均已知;
递归时从叶子节点开始标记节点被监控的状态,那么在遍历过程中遇到的二叉子树都可以看成有一个'根节点'和左右子树组成:
3 X Y X
/ -> / / /
9 20 Y Y Y X Y Z
对应每个遍历到的'根节点'和左右子树组,都可能包含下面情况:
- 左右子节点没有 X,但是是 Y,根节点只能放置 X
- 左右子节点中有(一个或者两个)X,则根节点自动变为 Y
- 根节点为 X,则左右子节点自动变为 Y
var minCameraCover = function(root) {
let _result = 0
// 递归到二叉树根节点如果其不能被子树的X监控需要在增加一个X
if (dfs(root) === 'Z') _result = _result + 1
function dfs(node) {
// 默认叶子节点不放置X,让其受其他节点监控
if (node == null) return 'Y'
let left = dfs(node.left),
right = dfs(node.right)
// 1. 如果左右节点都是Y,则根节点只能放置X
if (left == 'Y' && right == 'Y') return 'Z'
// 2. 如果左右节点中有1个或者2个X,那么根节点自动变成Y(注意此时左右节点不能有未受其他节点影响的子节点)
if (left !== 'Z' && right !== 'Z' && (left === 'X' || right === 'X')) {
return 'Y'
}
// 3. 如果左右节点均为查询到安装X和被监控Y,则在根节点放置X
_result++
return 'X'
}
return _result
}
递归
对于每个节点 root ,需要维护三种类型的状态:
- 状态 a:root 必须放置摄像头的情况下,覆盖整棵树需要的摄像头数目。
- 状态 b:覆盖整棵树需要的摄像头数目,无论 root 是否放置摄像头。
- 状态 c:覆盖两棵子树需要的摄像头数目,无论 root 本身是否被监控到。
设其左右孩子 left, right,对应的状态变量分别为(
,
,
)、(
,
,
):
- a =
+
+ 1;
- b = min(a,
+
,
+
)
因为定义 c 是 root 的右子树又被拆分左右子树,则 b 逻辑上是大于等于 c 的,a 包含了左右子树的
、
的情况则逻辑上是大于等于 b 的则:
覆盖当前子树:
- 根节点需要放置摄像头(a),摄像头总数等于:
- 根节点上的一个 1 + 覆盖整个左子树摄像头数
+ 覆盖整个右子树摄像头数
- 根节点上的一个 1 + 覆盖整个左子树摄像头数
+ 覆盖整个右子树的左右子树的摄像头数
(因为
包含了 root 放置的情况,所有根节点可能重复放置所有该组合一定大于上一组)
- 覆盖整个子树摄像头数(b),摄像头总数等于:
- 根节点节点放置摄像头形成全覆盖 a
- 左节子树根节点放置摄像头形成全覆盖
+ 右节子树根节点放置摄像头形成全覆盖
- 左节子树形成全覆盖,但不一定是左子树根节点放置摄像头
+ 右节子树根节点放置摄像头形成全覆盖
- 左节子树根节点放置摄像头形成全覆盖
+ 右节子树形成全覆盖,但不一定是右子树根节点放置摄像头
- 左节子树形成全覆盖,但不一定是左子树根节点放置摄像头
+ 右节子树形成全覆盖,但不一定是右子树根节点放置摄像头
+ 1
- 因为 a≥b≥c 所有后面两种组合其实可以忽略
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @return {number}
*/
var minCameraCover = function(root) {
function dfs(root) {
if (!root) {
return [Math.floor(Number.MAX_SAFE_INTEGER / 2), 0, 0]
}
const [la, lb, lc] = dfs(root.left)
const [ra, rb, rc] = dfs(root.right)
const a = lc + rc + 1
const b = Math.min(a, la + rb, lb + ra, la + ra, lb + rb + 1)
const c = Math.min(a, lb + rb)
return [a, b, c]
}
return dfs(root)[1]
}
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 一年经验Java开发0713面试
- 【分享】MicroBlaze大内部存储器(AXI BRAM)设计
- Spring Beans 自动装配
- Python将txt文件内容转换成列表
- 刷题记录-猿辅导2020
- redis stream(一):stream命令入门
- os.path.basename()
- 利用frida与ida实战脱壳360加固
- 你知道C语言中的危险函数吗?
- python与安全(四)shell反弹
- 怎么快速修复 bug ?
- 变量类型(cpu/gpu)
- 【Go语言学习】匿名函数与闭包
- Python中两种UnboundLocalError的解决方法
- 传智播客OA项目学习--阶段二(实体设计及技巧)