当前位置: 首页 > article >正文

力扣-968监控二叉树(Java贪心详细题解)

题目链接:968. 监控二叉树 - 力扣(LeetCode)

前情提要:

本题是一道名副其实的hard题目,他考察二叉树和贪心的综合运用能力。

所以我们不仅要会贪心还要会二叉树的一些知识,如果没有写二叉树类型的题目,建议大家该题可以放放,去刷其他的题目。

因为本人最近都来刷贪心类的题目所以该题就默认用贪心方法来做。

贪心方法:局部最优推出全局最优。

如果一个题你觉得可以用局部最优推出全局最优,并且没有反例来反驳的话就可以用贪心来试试。

题目思路:

本题要求监控树所有节点的最小摄像头数量。我们如何求这个最小摄像头数量呢?

一般遇到二叉树类的题目,遍历顺序很重要,我们如何确认这个遍历顺序呢?

确认遍历顺序

其实从案例我们也会发现,摄像头都没有放在叶子节点,而是放在了叶子节点的父节点。

首先我们要知道一个摄像头所监控的最大范围就是三层。

那么我们怎么最大限度的使用这个条件呢?这就是贪心所在。

我们尽量在叶子节点的父节点设为摄像头 这样可以最大限度的使用摄像头监控三层的条件。

以这个题局部贪心:再每一个叶子节点前面放一个摄像头,再每隔俩个节点再放一个摄像头。

全局最优:所得的摄像头的就是最少的。

那么肯定有人想叶子节点的父节点设摄像头 那我们可不可以在根节点的孩子节点设摄像头呢?

理论也行,但是本题要求最少的摄像头,我们就要最大限度的节省摄像头,由于一个二叉树叶子节点肯定大于等于根节点,处于节约叶子节点的摄像头而言,我们应该从叶子节点出发。

遍历顺序咱们就定下来 就是后序 从叶子节点向上返回结果为父节点。

那么我们如何隔两个节点放一个摄像头

此时需要分析每个节点的状态 三种情况的判定 0 无覆盖 1摄像头 2覆盖。

所以我们得根据左右孩子节点的状态来确认父节点的状态。

但是我们要对二叉树内的null值进行一个特判,null值应该赋给什么状态呢? 答案是赋值为2。为什么赋值为2呢。我们下面讲。

1.左右孩子节点全为2状态 父节点就为 0。

在这里插入图片描述

这里可能互相会想为什么状态为0 为什么不是1 这是因为我们是后序遍历,回溯的过程是从下到上,当孩子节点状态为2 肯定是孩子节点的下层节点出现了 1 此时我们本着最大节约摄像头的原则,他们的父节点就为0,由父节点的父节点为摄像头来将父节点覆盖。

这时我们就想通为什么null值设为2了,当一个孩子为叶子节点,他下一层返回的状态就是俩个2,那他本身应该就是未被覆盖的状态,因为本题的贪心思路就是让叶子节点都为非覆盖状态,让他的父节点为摄像头来监控下面三层的情况。

2.任意孩子节点为1 父节点就是2。

如果任意一个孩子节点被摄像头监控,那父节点就是被覆盖的范围,因为摄像头监控的范围未3层,他可以覆盖他上一层的也就覆盖父节点。

3.任意孩子为 0 父节点就是1。

在这里插入图片描述

如果是以下情况,则中间节点(父节点)应该放摄像头:

  • left == 0 && right == 0 左右节点无覆盖
  • left == 1 && right == 0 左节点有摄像头,右节点无覆盖
  • left == 0 && right == 1 左节点有无覆盖,右节点摄像头
  • left == 0 && right == 2 左节点无覆盖,右节点覆盖
  • left == 2 && right == 0 左节点覆盖,右节点无覆盖

这个不难理解,毕竟有一个孩子没有覆盖,父节点就应该放摄像头。

特判第四种方式 如果根节点为无覆盖 则将根节点设为1 即设为摄像头。

在这里插入图片描述

所以状态分析完了就可以开始写代码了

最终代码:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    //result用来记录结果
    int result = 0;
    public int minCameraCover(TreeNode root) {
        //temp用来判定第四种情况
        int temp = dfs(root);
        if(temp == 0){
            result ++;
        }
        return result;
        
    }
    public int dfs(TreeNode root){
        //递归三部曲
        //第一步确定方法的返回值和参数
        //后序需要子节点的状态来确认父节点的状态,所以我们需要将返回值设为int
        //第二步确定终止条件
        //遍历树最终会遇到null节点 所以终止条件就是遇到null就不要向下递归了 就该向上返回状态值了
        //第三步确定单层循环逻辑
        if(root == null)return 2;
        //遍历顺序 后序 左右中 即先递归左边 再递归右边 最后处理中间节点
        //左
        int left = dfs(root.left);
        //右
        int right = dfs(root.right);
        //三种情况的判定 0 无覆盖 1摄像头 2覆盖
        //1.当孩子节点全为 2 父节点就为0
        //2.当孩子节点有一个为0 父节点就为1
        //3.当孩子节点有一个为1 父节点就为 2
        if(left == 2&&right == 2)return 0;
        if(left == 0 ||right == 0){
            result ++;
            return 1;
        }
        if(left == 1 ||right == 1)return 2;
        return 0;
    }
}

这到题确实很难,所以需要大家多琢磨琢磨,多模拟几遍。

这一篇博客就到这了,如果你有什么疑问和想法可以打在评论区,或者私信我。

我很乐意为你解答。那么我们下篇再见!


http://www.kler.cn/a/289620.html

相关文章:

  • 前端垂直居中的多种实现方式及应用分析
  • golang如何实现sse
  • Elastic Observability 8.16:增强的 OpenTelemetry 支持、高级日志分析和简化的入门流程
  • 在Java中使用ModelMapper简化Shapefile属性转JavaBean实战
  • Flutter Getx状态管理
  • Springboot 日志处理(非常详细)
  • 用于基于骨架的动作识别的空间时间图卷积网络 ST-GCN (代码+数据集+模型)
  • 使用 Spring Cloud 有什么优势?
  • python读取excel数据详细解说
  • 缺失值插补解释:六种插补方法?
  • opencv学习时常用linux命令
  • ArkUI进阶-性能优化
  • Windows安装anaconda注意事项及jupyter notebook更换目录
  • 网站安全问题整改
  • Linux之grep命令
  • C++重载实现Mystring
  • qt5.15.2 模拟LVGL8.3
  • DataX导入或导出hive数据
  • 读书学习笔记入门 # Datawhale X 李宏毅苹果书 AI夏令营
  • 人活着的意义是什么
  • 监控平台之pvuv/点击事件/路由上报
  • 树莓派扩展RGB点阵屏的使用
  • vue-----window.open打开新窗口文件并且修改窗口标题下载文件
  • 云微客短视频矩阵系统,如何让企业赢在起跑线?
  • Flask框架默认session处理机制
  • gRPC学习之六:gRPC-Gateway集成swagger