https://leetcode-cn.com/problems/binary-tree-cameras/

难度:困难


给定一个二叉树,我们在树的节点上安装摄像头。

节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。

计算监控树的所有节点所需的最小摄像头数量。

示例 1:

输入:[0,0,null,0,0]

输出:1

解释:如图所示,一台摄像头足以监控所有节点。

示例 2:

输入:[0,0,null,0,null,0,null,null,0]

输出:2

解释:需要至少两个摄像头来监视树的所有节点。 上图显示了摄像头放置的有效位置之一。

提示:

  1. 给定树的节点数的范围是 [1, 1000]
  2. 每个节点的值都是 0。

解法1:递归+状态判断

仔细思考,根据题目,我们可以知道,对于一个节点 node,它只可能会存在三种状态(下图中红色代表安装监控器,蓝色代表被监控到,绿色代表未被监控到,或是监控与未监控两者都可):

  • node 任意一个孩子结点已安装监视器,那么当前 node 节点也会被监控到,这种状态记为 1

    关于下面右孩子的蓝色,这边解释一下,因为我们是从下往上遍历的,且因为一些条件判断,我们会保证遇到这种情况时,孩子结点会处于监控状态,具体可看代码。

  • node 任意一个孩子节点未安装监视器,且该孩子节点也未被监控到,那么当前节点必须安装监视器,这种状态我们记为 2

  • node 的孩子节点都没安装监视器,但是都被监控到了,那么当前节点可以安装监视器,也可以不安装监视器,但是本着节省资源的目的,需要将这种情况返回给父节点,让父节点做裁断(到底是父节点安装,还是当前节点安装)。

    JS 代码:

    var minCameraCover = function(root) {let res = 0;const dfs = (node) => {if (!node) { // 对于空节点,默认其是未安装监视器且被监控到的状态return 3;}let left = dfs(node.left), right = dfs(node.right);// 若有一个孩子结点未被监控到,那么当前节点需要安装监控器,造成的结果就是返回父节点时,表现是安装了监控器if (left === 2 || right === 2) {res++;return 1;}// 否则,若有一个孩子结点安装了监控器,当前节点会处于被监控的状态,且上面的判断语句已经排除了有孩子结点未被监控到的情况,因此当前节点返回到其父节点的表现就是没装监控器但是被监控到的情况if (left === 1 || right === 1) {return 3;}// 否则,当前节点会处于未装监控器且未被监控到的情况,也就是状态 2.return 2;};if (dfs(root) === 2) {res++;}return res;
    };
    

解法2:递归

解法2是官方的解题思路,看了几遍才看懂…因此附上一些自己的理解。

约定:若某棵树的所有节点都被监控,则称这棵树被覆盖

设当前节点为 root,其左右孩子为 leftright 。若想要覆盖以 root 为根的树,那么有两种情况:

  • 情况1:若给 root 设置了摄像头,那么其孩子节点 leftright 也会被监控到。此时只需要保证 leftright 两棵子树也被覆盖即可。
  • 情况2:若 root 没有设置摄像头,那么除了要保证以left为根结点的树 和 以right为根结点的树被覆盖之外,还需要保证 leftright 之中至少有一个节点安装了摄像头,这样 root 才能被监控到。

根据这两种情况,我们可以分析出,对于每个 root ,需要维护三种类型的状态:

  • 状态 aroot 必须安装摄像头的情况下(即情况1),覆盖整棵树需要的摄像头数量;
  • 状态 b :不论 root 是否安装摄像头,覆盖整棵树需要的摄像头数量;(也就是若 root 安置摄像头的话最好,不然 leftright 两个节点中需要至少一个有安置摄像头来保证 root 被监控)
  • 状态 c :不论 root 是否被监控到,覆盖两棵子树需要的摄像头数量;(对应着情况 1 和情况 2 中, left树 和 right树被覆盖,而root 是否被监控表示 leftright 节点可能有安置摄像头,也可能未安置摄像头)

根据以上定义,一定有 a >= b >= c

root 节点的左右孩子节点leftright维护的状态变量为:(la,lb,lc)(l_a,l_b,l_c)(la​,lb​,lc​) 和 (ra,rb,rc)(r_a,r_b,r_c)(ra​,rb​,rc​) ,那么:

  • a=lc+rc+1a = l_c + r_c + 1a=lc​+rc​+1 :即左右子树被覆盖所需的摄像头数量加上 root 安置的 1 个摄像头
  • b=min(a,min(la+rb,lb+ra))b = min(a, min(l_a + r_b, l_b + r_a))b=min(a,min(la​+rb​,lb​+ra​)) :即两种情况中的最小值,情况 2 中需要 leftright 中至少一个节点设置摄像头来保证 root 被监控,因此是 left 设置 + right 未设置left 未设置 + right 设置 这两种取值中的最小值。

对于状态 c 而言,只需要保证 leftright 两棵子树被覆盖即可(包括 leftright)。

那么,要么 root 处设置一个摄像头(保证 leftright 这两个节点被监控)的情况下leftright 两棵子树刚好也被覆盖,那么就是状态a 的摄像头数量;

要么 root 处不放置摄像头,leftright 两棵子树保证自己被覆盖,所需的摄像头数量就是 lb+rbl_b + r_blb​+rb​。

需要额外注意的是,对于 root 而言,如果其某个孩子为空,则不能通过在该孩子处放置摄像头的方式,监控到当前节点。因此,该孩子对应的变量 a 应当返回一个大整数,用于标识不可能的情形。

最终,根节点的状态变量 b 即为要求出的答案。

var minCameraCover = function(root) {const 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, Math.min(la + rb, ra + lb));const c = Math.min(a, lb + rb);return [a, b, c];}return dfs(root)[1];
};

LeetCode 968. 监控二叉树相关推荐

  1. 【算法】贪心算法:LeetCode 714 买卖股票的最佳时机含手续费 、LeetCode 968 监控二叉树

    LeetCode 714 买卖股票的最佳时机含手续费 (中等) 题目 描述 给定一个整数数组 prices,其中第 i 个元素代表了第 i 天的股票价格 :整数 fee 代表了交易股票的手续费用. 你 ...

  2. LeetCode 968. 监控二叉树(DFS)

    文章目录 1. 题目 2. 解题 1. 题目 给定一个二叉树,我们在树的节点上安装摄像头. 节点上的每个摄影头都可以监视其父对象.自身及其直接子对象. 计算监控树的所有节点所需的最小摄像头数量. 示例 ...

  3. 【leetcode 968. 监控二叉树】解题报告

    解题思路: 由于叶子节点一定不要安装监视器,这样才能使总监视器数量比较少,因此需要从下往上进行判断当前节点的状态(共:3种状态): 0: 当前节点安装了监视器 1: 当前节点可观,但没有安装监视器 2 ...

  4. 【leetcode困难】968. 监控二叉树

    968. 监控二叉树 瞎**分析评论区Rui大佬的答案,这题想直接递归return min还是有坑的,分计数和状态.有个状态转换的思想 转载于:https://www.cnblogs.com/yuel ...

  5. _32LeetCode代码随想录算法训练营第三十二天-贪心算法 | 738.单调递增的数字 、714.买卖股票的最佳时机含手续费、968.监控二叉树

    _32LeetCode代码随想录算法训练营第三十二天-贪心算法 | 738.单调递增的数字 .714.买卖股票的最佳时机含手续费.968.监控二叉树 题目列表 738.单调递增的数字 714.买卖股票 ...

  6. 力扣刷题day32|738单调递增的数字、714买卖股票的最佳时机含手续费、968监控二叉树

    文章目录 738. 单调递增的数字 思路 难点:遍历顺序 难点:设置flag 714. 买卖股票的最佳时机含手续费 贪心思路 难点 968. 监控二叉树 思路 难点:如何隔两个节点放一个摄像头 738 ...

  7. 力扣算法JS LC [714. 买卖股票的最佳时机含手续费] LC [968. 监控二叉树]

    LC 714. 买卖股票的最佳时机含手续费 给定一个整数数组 prices,其中 prices[i]表示第 i 天的股票价格 :整数 fee 代表了交易股票的手续费用. 你可以无限次地完成交易,但是你 ...

  8. 代码随想录算法训练营第37天|738. 单调递增的数字,714. 买卖股票的最佳时机含手续费,968. 监控二叉树

    Day 37 738. 单调递增的数字 class Solution { public:int monotoneIncreasingDigits(int n) {string strN = to_st ...

  9. D37 738.单调递增的数字 968.监控二叉树 + 贪心算法总结

    738.单调递增的数字 1.题目 给定一个非负整数 N,找出小于或等于 N 的最大的整数,同时这个整数需要满足其各个位数上的数字是单调递增. (当且仅当每个相邻位数上的数字 x 和 y 满足 x &l ...

最新文章

  1. 『Tarjan算法 无向图的双联通分量』
  2. APMServ 5.2.0 服务器平台搭建工具
  3. 2020年北大中文核心期刊目录_中文核心期刊目录汇总(2020年4月发布)
  4. centos7开启vnc服务_Centos7 VNC远程桌面服务安装配置
  5. “天下第一长联”与“元跨革囊”
  6. 关于视频光端机调制方式及介质特点的介绍
  7. 前端学习(1650):前端系列实战课程之字符串常用方法
  8. 在.net中序列化读写xml方法的总结(转载)
  9. 自定义ActionBar、Toolbar布局(解决自定义ActionBar布局左边始终有一点边距的问题)
  10. 测试基础-05-bug的定义生命周期
  11. AWS 推出长期支持的 OpenJDK 免费分发版本 —— Amazon Corretto
  12. Docker生产环境配置——设置direct-lvm模式
  13. zabbix从入门到精通之---Zabbix proxy的配置(一)
  14. 计算机三级的英语单词,大学英语三级词汇表(新版)资料.doc
  15. 【日常记录】CTF审查清单(linux)
  16. ZOJ 3797 Sister's Noise 组合+DP
  17. RAID的概念和RAID对于SQL性能的影响
  18. 机器学习中为什么需要验证集,验证集与测试集的区别是什么?
  19. 排序算法之简单插入排序法
  20. LeetCode 区间子数组个数

热门文章

  1. C语言嵌入式系统编程修炼之道——性能优化篇
  2. 你最讨厌的同事来自哪家公司?这家公司被上千个程序员怒怼!
  3. 昨天晚上,亲眼目睹了我一哥们的崩溃,被空降90后上司鄙视,说他这种人在公司......
  4. 原来书中说的JVM默认垃圾回收器是错的!
  5. 深度学习在阿里B2B电商推荐系统中的实践
  6. 如何在10亿个整数中找出前1000个最大的数?
  7. 不懂股权架构的创业,都是耍流氓!
  8. 这里有最全的k8s初学者指南!!!
  9. 美团面试失败(Java开发)
  10. 控制~线性系统~的能控性和能观性