DAY41:贪心算法(十)监控二叉树

news2024/9/22 23:27:58

文章目录

    • 968.监控二叉树
      • 思路
        • 遍历顺序
        • 空节点处理
        • 情况列举
      • 最开始的写法
        • debug测试:travelsal的输出多了1
      • 修改版
      • 二叉树注意点
      • 时间复杂度
      • 总结

968.监控二叉树

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

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

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

在这里插入图片描述
输入:[0,0,null,0,0]
输出:1
解释:如图所示,一台摄像头足以监控所有节点。

在这里插入图片描述
输入:[0,0,null,0,null,0,null,null,0]
输出:2
解释:需要至少两个摄像头来监视树的所有节点。 上图显示了摄像头放置的有效位置之一。

提示:

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

思路

本题一个重要思路是,二叉树中一定是叶子节点数量大于根节点数量的。叶子节点和根节点比起来,数量呈现指数级的扩大,因此我们想要用最少的摄像头,就要在叶子节点上优先想如何节约摄像头

在叶子节点的情况下,我们要优先在叶子节点的父节点上放摄像头,然后一层一层往上推,往上推的逻辑是每隔2个节点放一个摄像头

遍历顺序

因为我们从叶子节点开始,从下往上处理二叉树,所以一定是后序遍历。遍历的同时,我们需要记录每个节点的状态,从而根据左右孩子的状态去确定父节点的状态

这里就涉及到一个状态转移的问题,但是本题的状态转移和动规的状态转移不同,本题只是涉及到一个每个节点状态的判断,并不是要得到最优状态。本题中节点有三种状态:

  • 0:无摄像头,无覆盖
  • 1:有摄像头
  • 2:无摄像头,有覆盖(在摄像头范围内)

我们在后序遍历的时候,可以通过状态转移,来判断左右孩子是某状态的时候,父节点应该是什么状态

空节点处理

本题的目的,就是尽可能让叶子节点的父节点去放摄像头,这样摄像头数目才能最少。为了让叶子节点的父节点能放摄像头,空节点应该是有覆盖的状态。

也就是说,遇到空节点应该向上返回2,这样才能让叶子节点父节点有摄像头。

情况列举

  • 当前节点左右孩子都无摄像头有覆盖,也就是状态2,此时当前节点必然是状态0

在这里插入图片描述

  • 当前节点左右孩子至少有一个无覆盖,此时当前节点一定要装一个摄像头,也就是状态1,否则就不能全覆盖了

在这里插入图片描述

  • 左右孩子至少有一个有摄像头,那么当前节点一定是2

在这里插入图片描述

  • 最后一种情况是基于第一种情况,第一种情况的当前节点无覆盖,需要等待父节点去把他覆盖。但是第一种情况的节点,如果是根节点,就没有父节点可以装摄像头!

    此时根节点本身必须装一个摄像头,否则根节点就是无覆盖的状态。

本题所有可能的情况就限于这四种。主要是分析出来前三种,第四种是遍历到最后对根节点进行单独的判断。

最开始的写法

class Solution {
public:
    //摄像头个数
    int result=0;
    //后序遍历,有返回值,返回当前节点状态
    int travelsal(TreeNode* root){
        //后序终止条件
        if(root==nullptr) return 2;//空节点返回状态2

        //左右中 后序遍历
        int left = travelsal(root->left);
        int right = travelsal(root->right);

        //判断情况,第一种,左右都有cover无摄像头 左右都是状态2
        if(left==2&&right==2){
            return 0;//无cover 返回0让父节点加摄像头
        }
        //第二种,左右有一个是0
        if(left==0||right==0){
            result++;
            return 1;
        }
        //第三种,左右有一个有摄像头
        if(left==1||right==1){
            return 2;
        }
        //逻辑不会走到这里,只是为了有返回值
        return -1;
    }
    int minCameraCover(TreeNode* root) {
        travelsal(root);
        cout<<travelsal(root)<<endl;
        //第四种,root无cover,但是没有父节点了
        if(travelsal(root)==0){
            result++;
        }
        return result;
    }
};

debug测试:travelsal的输出多了1

存在逻辑问题,travelsal的输出多了1

在这里插入图片描述
但是实际上,后序遍历的逻辑是正确的,但是主函数写法出了问题

报错的主函数:

int minCameraCover(TreeNode* root) {
     travelsal(root);
     cout<<travelsal(root)<<endl;
     //第四种,root无cover,但是没有父节点了
     if(travelsal(root)==0)
     //if(state==0)
        result++;
     return result;
    }

注意这里的问题是,函数重写一次就会重复运行一次,上面这段主函数代码,相当于travelsal运行了三次

第一次是travelsal(root);,第二次是cout<<travelsal(root)<<endl;,第三次是if(travelsal(root)==0)函数只要出现就会运行一次,而result是一个全局变量,每次运行都会在之前运行的基础上+1.

因此,这里才会出现全局变量是3,但是函数实际逻辑正确的情况,就是因为函数写了三次所以运行了三遍。(这是一个很基础的逻辑问题,函数重新写一次就会运行一次,即使是在cout里面重写,也会运行第二遍,希望下次不要犯这种离谱错误了)

修改版

  • 把主函数写法改掉就可以了
class Solution {
public:
    //摄像头个数
    int result=0;
    //后序遍历,有返回值,返回当前节点状态
    int travelsal(TreeNode* root){
        //后序终止条件
        if(root==nullptr) return 2;//空节点返回状态2

        //左右中 后序遍历
        int left = travelsal(root->left);
        int right = travelsal(root->right);

        //判断情况,第一种,左右都有cover无摄像头 左右都是状态2
        if(left==2&&right==2){
            return 0;//无cover 返回0让父节点加摄像头
        }
        //第二种,左右有一个是0
        if(left==0||right==0){
            result++;
            return 1;
        }
        //第三种,左右有一个有摄像头
        if(left==1||right==1){
            return 2;
        }
        //逻辑不会走到这里,只是为了有返回值
        return -1;


    }
    int minCameraCover(TreeNode* root) {
        int state = travelsal(root);
        cout<<state<<endl;
        //第四种,root无cover,但是没有父节点了
        //if(travelsal(root)==0)
        if(state==0)
            result++;
            return result;
    }
};

或者主函数可以写成

  • 比较简洁的写法,return也可以直接返回表达式,不容易出错
int minCameraCover(TreeNode* root) {
     int state = travelsal(root);
     return result+=(state==0?1:0);
}

二叉树注意点

  • 前后序的区别主要在处理节点的顺序,处理当前节点需要下面节点的状态就会用后序,但是所有遍历的终止条件都是遇到空节点返回(也就是触底了才会return),前序也是遇到空节点才开始return
  • 最后return -1 是因为逻辑不会走到这里但是函数需要一个返回值,return什么都可以

时间复杂度

  • 时间复杂度: O(n),需要遍历二叉树上的每个节点
  • 空间复杂度: O(n),本题的空间复杂度在于递归的返回值return输入的二叉树本身是不算空间复杂度的,但是每遍历到一个节点都会return 0/1/2状态值,也就是说每次递归的返回值都占了一个int的内存。所以空间复杂度是return的数值,一共n个数值所以是O(n)

总结

本题的难点首先是要想到贪心的思路,要想得到最少摄像头,就必须在叶子节点的父节点上面放摄像头

然后就是遍历和状态推导,首先要想到二叉树每个节点,都可以归结为三种状态

第二,要想到二叉树每个叶子节点的状态去推当前节点的状态,一共可以归纳为四种情况;然后通过遍历在二叉树上进行状态推导,后序遍历的同时接收下面叶子节点的状态,从而判断出当前的状态和需不需要放置摄像头。

难点在于情况的分析,不要想复杂,所有的情况就是3+1种(最后一种是根节点单独考虑)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/728550.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot多模块项目整合JPA,解决被依赖模块Dao层无法注入的问题

DAO层&#xff1a; 报错&#xff1a; 解决办法&#xff1a;被依赖模块的DefaultConfig类加上如下注解&#xff0c;问题即解决 ComponentScan(basePackages "com.fdw.study.*") Configuration EnableJpaRepositories(value "com.fdw.study.dao") EntityS…

Linux0.11内核源码解析-read_write.c

目录 sys_lseek read write read_write.c主要是实现文件系统调用read(),write()和lseek()三个功能 read和write函数分别是调用file_dev.c/pipe.c/block_dev.c/char_dev.c实现相对应的函数 sys_lseek lseek实现系统调用将对文件句柄对应文件结果体中的当前读写指针进行修改&…

matplot 显示文本

import matplotlib.pyplot as plt# 绘制一些数据 x_data [1, 2, 3, 5] y_data [2, 4, 8, 10] plt.plot(x_data, y_data,o,markersize 20)# 添加文本注释 text [a,b,c,d] for i in range(4):plt.text(x_data[i], y_data[i],text[i], fontdict{family: serif, size: 16, col…

leetcode极速复习版-第五章栈与队列

目录 栈与队列 理论基础 232.用栈实现队列 225. 用队列实现栈 20. 有效的括号 1047. 删除字符串中的所有相邻重复项 150. 逆波兰表达式求值 239.滑动窗口最大值 347.前 K 个高频元素 栈与队列 理论基础 队列是先进先出&#xff0c;栈是先进后出。 232.用栈实现队列 使用栈实现队…

动态路由,微信小程序绑定

■登录成功之后添加动态路由 ●登录的时候会获取到它的菜单配置■动态路由 | Vue Router <view wx:for"{{list}}">{{index}}--- {{item.name}} </view><view wx:for"{{list}}" wx:for-item "ttt" wx:for-index"num"&…

第二节 给SpringBootAdmin的server端加入spring security安全控制

前言 本来想用一节就写完SpringBootAdmin的&#xff0c;但随着研究的深入发现一节应该是不够的&#xff0c;网上的资料也不会非常系统&#xff0c;官网的例子有些已经好几年没更新了&#xff0c;所以接下来还是系统性的来写下吧 第一节 完成基础配置&#xff0c;暴露所有端点…

Qt-->QQ登陆界面图形化界面

#include "mainwindow.h" #include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow) {ui->setupUi(this);this->setFixedSize(640,520);//设置窗口标题this->setWindowTitle("QQ&qu…

勒索病毒危害,企业该如何预防勒索病毒

勒索病毒是一种恶意软件&#xff0c;它会对企业内的计算机系统或数据进行加密或锁定&#xff0c;并要求企业支付赎金以解锁或解密。 勒索病毒危害&#xff1a; 数据丢失&#xff1a;勒索病毒可以加密您的文件、照片、视频和其他重要数据&#xff0c;使其无法访问或恢复。如果…

解决centos7和主机win11不能互传文件复制粘贴;及CentOS7最小安装版 VMware Tools安装

linux.iso 中的文件已经加载到 /mnt/cdrom 目录下了。在 /mnt/cdrom 中找到加载出来的文件并拷贝到 /tmp目录下&#xff0c;进行解压。解压好后从中找到 vmware-install.pl&#xff0c;这是vmware tools的安装文件&#xff0c;执行此文件开始安装 ./vmware-install.pl 一路Ente…

宏基因组碳循环高分分析思路,你值得拥有!

碳是生命物质中的主要元素之一&#xff0c;是有机质的重要组成部分。地球上主要有四大碳库&#xff0c;即大气碳库&#xff0c;海洋碳库、陆地生态系统碳库和岩石圈碳库。碳循环&#xff0c;是指碳元素在自然界的循环状态&#xff0c;大气中的二氧化碳&#xff08;CO2&#xff…

Shamir秘密共享

目录 Shamir秘密共享 秘密共享的概念 问题1: 问题2: 秘密分割门限方案的定义 Shamir秘密共享方案 组成 构造思路 构造 计算f(x) 例1 例2 二、GMW方案 Shamir秘密共享 秘密共享的概念 问题1: 保险柜中存放有10个人的共有财产&#xff0c;要从保险柜中取出物品&am…

【Python编程系列】5、变量

作用 变量用来存储数据: a = 10使用规则 python作为解释型,属于弱类型和动态型语言。它不需要强制声明变量的数据类型,因为它不需要编译。一般要编译的语言需要明确所有的东西,只有这样才能编译成二进制文件。 由于不需要声明数据类型,那么在声明变量时会一并赋值: …

什么是serialVersionUID?为什么要使用它?

目录 一、什么是serialVersionUID二、创建一个serialVersionUID三、使用 serialVersionUID3.1 序列化实例程序3.2 反序列化实例程序3.3 serialVersionUID不同的情况下进行序列话和反序列化3.4 能不能不提供serialVersionUID&#xff1f; 一、什么是serialVersionUID SerialVer…

Dubbo hystrix 熔断降级 详细示例 多服务 公共api

目录 介绍 demo-api pom 目录 代码api provider 服务提供者 目录 pom 服务实现代码 启动代码 配置 日志 consumer 消费者 目录 pom 调用service接口 调用serviceImpl类 ctr 配置 页面调用熔断效果 相关文章 介绍 因为网上和官网拷贝的文档发现有很多版本…

axios拦截器和token

axios拦截器 ​​ 在请求或响应被 then 或 catch 处理前拦截它们。 // 添加请求拦截器 axios.interceptors.request.use(function (config) {// 在发送请求之前做些什么return config;}, function (error) {// 对请求错误做些什么return Promise.reject(error);});// 添加响应…

阿里云国际站代理商:阿里巴巴的阿里云到底是做什么用的,怎么我们普通人不理解,也不知道是怎么样一种服务?

阿里巴巴的阿里云到底是做什么用的&#xff0c;怎么我们普通人不理解&#xff0c;也不知道是怎么样一种服务&#xff1f;   阿里巴巴的阿里云&#xff0c;不只是一个名字——打通理解的迷宫   面对朝日逐极而生的科技天光&#xff0c;阿里巴巴如一座灯塔引领者我们向前。然…

openGauss学习笔记-02 openGauss系统架构

文章目录 openGauss学习笔记-02 openGauss系统架构2.1 软件架构2.1.1 逻辑架构2.1.2 逻辑架构说明 openGauss学习笔记-02 openGauss系统架构 openGauss是单机系统&#xff0c;在这样的系统架构中&#xff0c;业务数据存储在单个物理节点上&#xff0c;数据访问任务被推送到服务…

nginx七层代理和四层转发的理解

先来理解一下osi七层模型 应用层 应用层是ISO七层模型的最高层&#xff0c;它直接与用户和应用程序交互&#xff0c;提供用户与网络的接口。它包括各种应用协议&#xff0c;如HTTP、FTP、SMTP等&#xff0c;用于实现特定应用的功能和通信表示层 表示层…

鉴机:粉丝买了一台宝峰uv5r对讲机,竟是假的?

最近有粉丝在某宝上购买了一台uv5r对讲机&#xff0c;回来拆盒后发现有点不对劲&#xff0c;甚至连logo都没有&#xff0c;请各位鉴别一下&#xff0c;看看是真的还是假的。 网友买的uv5r对讲机 其他网友看完后几乎一致认为是假的&#xff0c;有人还贴出来比对照片&#xff1a…

系统版本由Centos7.9恢复到RedHat7.5

问题描述 安装nginx的时候无法联网&#xff0c;通过rpm包进行的安装&#xff0c;打包编译时使用的部分依赖包是centos的资源&#xff0c;导致安装后系统版本由之前的RedHat7.5变成了centos7.9。现需恢复到之前的系统版本。 解决方法 将之前安装的centos资源包删除&#xff0c…