代码随想录训练营 Day31打卡 贪心算法 part05 56. 合并区间 738. 单调递增的数字 968. 监控二叉树

news2024/12/26 18:34:36

代码随想录训练营 Day31打卡 贪心算法 part05

一、 力扣56. 合并区间

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。
示例
输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].

先排序,让所有的相邻区间尽可能的重叠在一起,按左边界,或者右边界排序都可以,处理逻辑稍有不同。

按照左边界从小到大排序之后,如果 intervals[i][0] <= intervals[i - 1][1] 即intervals[i]的左边界 <= intervals[i - 1]的右边界,则一定有重叠。(本题相邻区间也算重贴,所以是<=)

如图,(注意图中区间都是按照左边界排序之后了):

在这里插入图片描述
知道如何判断重复之后,剩下的就是合并了,如何去模拟合并区间呢?

其实就是用合并区间后左边界和右边界,作为一个新的区间,加入到result数组里就可以了。如果没有合并就把原区间加入到result数组。

代码实现

class Solution:
    def merge(self, intervals):
        result = []  # 用于存放合并后的区间结果
        if len(intervals) == 0:
            return result  # 如果区间集合为空,直接返回空的结果集

        intervals.sort(key=lambda x: x[0])  # 按照区间的左边界进行排序,这样可以保证后续区间的顺序性

        result.append(intervals[0])  # 将排序后的第一个区间直接加入结果集

        # 从第二个区间开始遍历
        for i in range(1, len(intervals)):
            # 如果当前区间的左边界小于或等于结果集中最后一个区间的右边界,说明区间重叠
            if result[-1][1] >= intervals[i][0]:
                # 合并区间:更新结果集中最后一个区间的右边界为当前区间和结果集中右边界的较大值
                result[-1][1] = max(result[-1][1], intervals[i][1])
            else:
                # 如果当前区间与结果集中最后一个区间不重叠,直接将当前区间加入结果集
                result.append(intervals[i])

        return result  # 返回合并后的区间结果集

力扣题目链接
题目文章讲解
题目视频讲解

二、 力扣738. 单调递增的数字

当且仅当每个相邻位数上的数字 x 和 y 满足 x <= y 时,我们称这个整数是单调递增的。
给定一个整数 n ,返回 小于或等于 n 的最大数字,且数字呈 单调递增
示例 1:
输入: n = 10
输出: 9
示例 2:
输入: n = 1234
输出: 1234

题目要求小于等于N的最大单调递增的整数,那么拿一个两位的数字来举例。

例如:98,一旦出现strNum[i - 1] > strNum[i]的情况(非单调递增),首先想让strNum[i - 1]–,然后strNum[i]给为9,这样这个整数就是89,即小于98的最大的单调递增整数。

从后向前遍历,就可以重复利用上次比较得出的结果了,从后向前遍历332的数值变化为:332 -> 329 -> 299

版本一

将输入整数 N 转换为字符串 strNum,方便逐位操作。
从右向左遍历字符串,检查每一位数字与前一位的关系。
如果发现当前位数字小于前一位,说明递增性被破坏,更新 flag 为当前的位置,并将前一位数字减1以修复递增性。
将从 flag 开始的所有位置上的数字改为9,确保生成的数字是最大且递增的。
最终,将修改后的字符串转换为整数返回。

class Solution:
    def monotoneIncreasingDigits(self, N: int) -> int:
        # 将整数N转换为字符串形式,方便逐位处理
        strNum = str(N)
        # flag用于标记从哪里开始将数字修改为9,初始值设置为字符串的长度,避免错误的更新
        flag = len(strNum)
        
        # 从右往左遍历字符串中的每一位
        for i in range(len(strNum) - 1, 0, -1):
            # 如果当前位的数字比前一位的数字小,说明递增性被破坏,需要调整
            if strNum[i - 1] > strNum[i]:
                flag = i  # 更新flag,记录需要修改的位置
                # 将前一位数字减1,并更新字符串
                strNum = strNum[:i - 1] + str(int(strNum[i - 1]) - 1) + strNum[i:]
        
        # 从flag位置开始,将其后的所有数字都变为9,以保证生成的数字最大且递增
        for i in range(flag, len(strNum)):
            strNum = strNum[:i] + '9' + strNum[i + 1:]
        
        # 最后将字符串转换回整数返回
        return int(strNum)

版本二

将输入整数 N 转换为字符列表 strNum,便于逐位修改。
如果当前位数字比前一位数字小,递增性被破坏。
将前一位数字减1,并将当前位置及其后面的数字全都设为9,保证最大化。
最后将字符列表转换为字符串,再转换为整数返回。

class Solution:
    def monotoneIncreasingDigits(self, N: int) -> int:
        # 将整数N转换为字符列表,便于逐位操作
        strNum = list(str(N))

        # 从右向左遍历字符串中的每一位
        for i in range(len(strNum) - 1, 0, -1):
            # 如果当前位数字比前一位数字小,说明递增性被破坏,需要调整
            if strNum[i - 1] > strNum[i]:
                # 将前一位数字减1
                strNum[i - 1] = str(int(strNum[i - 1]) - 1)
                # 将当前位置及其之后的所有数字都设为9,确保递增性并最大化
                for j in range(i, len(strNum)):
                    strNum[j] = '9'

        # 将字符列表转换回字符串,再转换为整数返回
        return int(''.join(strNum))

版本三 贪心法 精简版

将整数 N 转换为字符串 strNum。
找到递增性被破坏的位置,如果找到,将前一位数字减1,后续所有位置设为9。
将字符串 strNum 转换为整数并返回。

class Solution:
    def monotoneIncreasingDigits(self, N: int) -> int:
        # 将整数N转换为字符串形式
        strNum = str(N)
        
        # 从右向左遍历字符串中的每一位
        for i in range(len(strNum) - 1, 0, -1):
            # 如果当前位数字比前一位数字小,递增性被破坏
            if strNum[i - 1] > strNum[i]:
                # 将前一位数字减1,并将当前位置及之后的所有数字设为9
                strNum = strNum[:i - 1] + str(int(strNum[i - 1]) - 1) + '9' * (len(strNum) - i)
        
        # 将最终的字符串转换为整数并返回
        return int(strNum)

力扣题目链接
题目文章讲解
题目视频讲解

三、 力扣968. 监控二叉树

给定一个二叉树,我们在树的节点上安装摄像头。
节点上的每个摄影头都可以监视 其父对象、自身及其直接子对象
计算监控树的所有节点所需的最小摄像头数量。
示例 1:
在这里插入图片描述
输入:[0,0,null,0,0]
输出:1
解释:如图所示,一台摄像头足以监控所有节点。
示例 2
在这里插入图片描述

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

本题我们要从下往上看,局部最优:让叶子节点的父节点安摄像头,所用摄像头最少,整体最优:全部摄像头数量所用最少!

如何隔两个节点放一个摄像头

来看看这个状态应该如何转移,先来看看每个节点可能有几种状态,我们分别有三个数字来表示:
0:该节点无覆盖
1:本节点有摄像头
2:本节点有覆盖

空节点的状态只能是有覆盖,这样就可以在叶子节点的父节点放摄像头了
主要有如下四类情况:

情况1:左右节点都有覆盖
左孩子有覆盖,右孩子有覆盖,那么此时中间节点应该就是无覆盖的状态了。

情况2:左右节点至少有一个无覆盖的情况
这个不难理解,毕竟有一个孩子没有覆盖,父节点就应该放摄像头。
此时摄像头的数量要加一,并且return 1,代表中间节点放摄像头。

情况3:左右节点至少有一个有摄像头
如果是以下情况,其实就是 左右孩子节点有一个有摄像头了,那么其父节点就应该是2(覆盖的状态)

情况4:头结点没有覆盖
以上都处理完了,递归结束之后,可能头结点 还有一个无覆盖的情况:
所以递归结束之后,还要判断根节点,如果没有覆盖,result++

代码实现

class Solution:
    # 贪心算法:从下往上安装摄像头,以确保安装数量最少,达到局部最优从而实现全局最优
    # 主要思路是跳过叶子节点,优先给叶子节点的父节点安装摄像头,并且每隔两层节点安装一个摄像头,直到根节点
    # 节点状态定义:
    # 0: 该节点未覆盖(需要安装摄像头)
    # 1: 该节点有摄像头
    # 2: 该节点已被覆盖(由子节点的摄像头覆盖)
    
    def minCameraCover(self, root: TreeNode) -> int:
        result = [0]  # 用于记录摄像头的安装数量,使用列表是为了在递归中修改该值

        # 调用递归函数,如果根节点未被覆盖,则需要在根节点安装一个摄像头
        if self.traversal(root, result) == 0:
            result[0] += 1

        return result[0]  # 返回最终安装的摄像头数量

    def traversal(self, cur: TreeNode, result: List[int]) -> int:
        if not cur:  # 如果当前节点为空,表示这个节点已被视为已覆盖(不需要安装摄像头)
            return 2

        # 递归处理当前节点的左子树
        left = self.traversal(cur.left, result)
        # 递归处理当前节点的右子树
        right = self.traversal(cur.right, result)

        # 情况1: 左右节点都有覆盖(但当前节点本身未被覆盖)
        # 如果左右子节点都处于已覆盖状态,但没有摄像头,当前节点需要摄像头来覆盖
        if left == 2 and right == 2:
            return 0  # 当前节点未被覆盖,返回0

        # 情况2: 当前节点的左右子节点至少有一个未被覆盖
        # 如果任意一个子节点未被覆盖,则当前节点需要安装摄像头
        elif left == 0 or right == 0:
            result[0] += 1  # 增加摄像头数量
            return 1  # 当前节点安装了摄像头,返回1

        # 情况3: 当前节点的子节点有一个摄像头覆盖,或者两个子节点都有摄像头覆盖
        # 在这种情况下,当前节点已经被覆盖,不需要额外安装摄像头
        else:
            return 2  # 当前节点已被覆盖,返回2

力扣题目链接
题目文章讲解
题目视频讲解

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

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

相关文章

【JavaEE】JVM 内存区域划分,以及 Java 垃圾回收机制引用计数器,可达性分析等

目录 1. JVM执行流程 2. JVM运行时数据区 2.1 堆 2.2 Java虚拟机栈(线程私有) 2.3本地方法栈(线程私有) 2.4 程序计数器 2.5 元数据区 3. JVM的类加载机制 1) 加载 2) 验证 3) 准备 4) 解析 5) 初始化 双亲委派模型 4. java垃圾回收 4.1 死亡对象判断方法 a) …

超精细CG杰作:8K壁纸级官方艺术插画,展现极致美丽与细节的汉服女孩

极致精美的数字艺术杰作&#xff1a;8K壁纸级别的官方插画&#xff0c;展现超高清细节与和谐统一的美感&#xff0c;女孩的精致面容与眼神在光影下熠熠生辉&#xff0c;汉服主题下的超高分辨率作品&#xff0c;文件巨大&#xff0c;细节丰富&#xff0c;令人惊叹。 正向提示词…

内存泄漏之如何使用Visual Studio的调试工具跟踪内存泄漏?

使用Visual Studio的调试工具跟踪内存泄漏是一个系统性的过程&#xff0c;主要包括启用内存泄漏检测、运行程序、分析内存使用情况以及定位泄漏源等步骤。 Visual Studio提供了多种方式来检测内存泄漏&#xff0c;你可以根据自己的需求选择合适的方法。 注意&#xff1a;下面…

父页面选项式api,子页面组合式api,子页面如何获取父页面的方法以及传值到将子页面的值传给父页面

开发的项目中是vue3的项目&#xff0c;但是有些同事用vue2中的选项式api写法&#xff0c;有些同事使用的是vue3组合式api的写法&#xff0c;此时子页面需要获取父页面的方法把数据传入父页面的方法中 父页面&#xff1a; 在父页面中order-item组件中创建自定义方法navigation和…

Leetcode每日刷题之剑指offer 57.和为s的两个数字(C++)

1.题目解析 现在题目改名为LCR.查找总价值为目标值的两个商品&#xff0c;虽然题目改变但是核心并未变化&#xff0c;都是需要寻找出和为指定数字的两数 2.算法原理 我们由题目知道给出的数组是递增的&#xff0c;所以在数组的首尾固定两个指针&#xff0c;判断其和是否为指定数…

Ceph篇之利用shell脚本实现批量创建bucket桶

Ceph创建bucket桶 在 Ceph 中创建桶&#xff08;bucket&#xff09;需要使用 Ceph 对象网关&#xff08;RGW&#xff09;。 注&#xff1a;如果查看shell批量创建脚本请直接参见目录3 1. 利用radosgw-admin工具创建桶 确保 Ceph 集群和对象网关已正确配置 确保你的 Ceph 集群…

快速了解Vi 和 Vim 编辑器三种模式及快捷键使用

&#x1f600;前言 本篇博文是关于Vi 和 Vim 编辑器的三种模式及快捷键使用&#xff0c;希望你能够喜欢 &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章可以帮助到大家&#xff0c;您的满意…

大数据产业链图谱_产业链全景图_大数据行业市场分析

数据作为新型生产要素&#xff0c;是数字化、网络化、智能化的基础&#xff0c;已快速融入生产、分配、流通、消费和社会服务管理等各环节&#xff0c;影响着千行百业&#xff0c;推动着我国数字经济的蓬勃发展。 大数据又称巨量数据、海量数据&#xff0c;是由数量巨大、结构…

C语言 | Leetcode C语言题解之第341题扁平化嵌套列表迭代器

题目&#xff1a; 题解&#xff1a; struct NestedIterator {int *vals;int size;int cur; };void dfs(struct NestedIterator *iter, struct NestedInteger **nestedList, int nestedListSize) {for (int i 0; i < nestedListSize; i) {if (NestedIntegerIsInteger(neste…

Sprache:轻量级C#解析器构建,可用于字符串验证等。

我们在开发中&#xff0c;经常需要对一些结构化文本进行解析&#xff0c;用于验证是否符合规则。我们一般会使用正则表达式&#xff0c;同时正则表达式也非常强大&#xff0c;但正则表达式在语法不便阅读与维护。 下面介绍一个简单、轻量级的库&#xff0c;方便我们在C#代码中…

React 学习——打包后,包体积可视化

1、安装插件 &#xff08; source-map-explorer &#xff09; npm i source-map-explorer 2、在配置文件package.json中加入 &#xff08; "analyze": "source-map-explorer build/static/js/*.js" &#xff09;&#xff0c;位置截图 "analyze&q…

Flask 线上高并发部署方案实现

目录 1、Flask默认多线程执行 2、使用gevent.pywsgi实现 3、是用uWSGI服务器实现 1、Flask默认多线程执行 前言&#xff1a;在Flask的较早版本中&#xff0c;默认并不支持多线程模式。然而&#xff0c;从Flask 0.9版本开始&#xff0c;引入了多线程模式的支持&#xff0c;并…

红酒与旅游攻略:旅行途中的风味之选

在旅行的道路上&#xff0c;我们总是渴望寻找那些能够触动心灵、留下深刻记忆的不同体验。而红酒&#xff0c;作为一种充满韵味和故事的饮品&#xff0c;无疑是旅行途中的风味之选。洒派红酒&#xff08;Bold & Generous&#xff09;&#xff0c;这款定制红酒&#xff0c;以…

基于xilinx IP的频域脉冲压缩算法的实现和matlab仿真

工具&#xff1a;matlabR2021b&#xff0c;vivado2018.3. 脉冲压缩的原理 脉冲压缩实际上就是对接收信号进行匹配滤波处理。根据发射的波形不同&#xff0c;脉冲压缩时选择不同的匹配滤波器系数。 数字脉冲压缩的实现方式有两种: 一是时域卷积法; 二是频域乘积法。依据傅里叶…

智能化包括自动化与非自动化

智能化通常指的是系统或设备具备智能功能&#xff0c;以提高其自主性和效率。智能化可以分为自动化与非自动化两大类&#xff0c;每一类都有其独特的特点和应用场景。 一、自动化 自动化指的是系统能够在无需人为干预的情况下完成任务或操作。自动化系统通常依赖于预设的规则、…

基于LangChain手工测试用例转接口自动化测试生成工具!

接口自动化测试用例是一个老生常谈的问题&#xff0c;在未引入人工智能之前&#xff0c;也有非常多的生成方案&#xff0c;比如如下所示&#xff0c;通过har生成接口自动化测试用例&#xff1a; 但是以上的生成方式依然是有一些弊端&#xff0c;比如 har 本身虽然能表述一定的接…

铁威马NAS教程丨TOS应用中心无法下载应用,显示0%或“准备中“?

故障排除 适用机型 所有 TNAS型号 原因分析 该现象通常是网络配置不正确或文件系统异常引起&#xff1a; 1.获取不到网关&#xff0c;状态栏甚至显示红色的“未连接” 2.路由器自动分配的DNS无法解析出下载服务器的域名 3.文件系统为只读文件系统 解决方法 1.重新获取…

中间件|day1.Redis

Redis 定义 Redis 是一个开源&#xff08;BSD许可&#xff09;的&#xff0c;内存中的数据结构存储系统&#xff0c;它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构&#xff0c;如 字符串&#xff08;strings&#xff09;&#xff0c; 散列&#xff08;hash…

TongHttpServer安装部署

TongHttpServer安装部署 毫无背景不是你堕落的理由&#xff0c;而更应该是你前进的动力。你该花时间思考&#xff0c;如何打好一副烂牌&#xff1b;而不是抱怨命运&#xff0c;或者干脆撕牌。 部署环境 TongHttpServer V6.0是一款轻量级负载均衡软件&#xff0c;支持 0SI 四层…

【瑞芯微RV1126(深度学习模型部署)】部署自己训练的yolov8-seg,实现足型检测!

前言 如果按照本系列第一篇博客那样交叉编译了opencv&#xff0c;那本文有些步骤就不用了&#xff0c;比如交叉编译工具链的下载&#xff0c;所以自己斟酌步骤。 本系列第一篇&#xff1a;https://blog.csdn.net/m0_71523511/article/details/139636367 本系列第二篇&#xff…