面试经典150题——用最少数量的箭引爆气球

"The only person you are destined to become is the person you decide to be." - Ralph Waldo Emerson

scenery of mountain

1. 题目描述

2.  题目分析与解析

这个题目开始读题的时候是有点不好理解题意的,因此我先做个图让大家对于题意有更好更直观的理解再来分析题目。

比如对于这个测试用例:

image-20240301092542335

实际上points就是气球的宽度,可视化如下:

image-20240301093354544

而题目要求的就是引爆所有气球所必须射出的 最小 弓箭数 ,对于上述情况,那么就需要以下红色的两支箭:

image-20240301093746553

其中虚线框就是表示两支箭的可以横向移动的范围。第一支箭从【2,6】都可以射出从而击破两个气球,第二支箭从【10,12】都可以射出击破剩下的两个气球。

2.1 思路一

可以看出本题的实质就是在找能够涵盖所有气球的最小交集个数。那么我们首先应该想到的是排序,经过排序后,然后遍历每一个区间,查看当前区间能够附带击破的气球的个数,比如如下图:

image-20240301094647448

首先遍历第一个区间【1,6】,查看每一个位置能够附带击破的气球的个数,在数字1的位置,发现只能附带击破自己本身1个气球,在【2,4】发现能击破两个,但是在【5,6】(因为等于5就可以击破蓝色气球:满足  xstart ≤ x ≤ x``end,则该气球会被 引爆 ),所以我们肯定得将箭设置成能击破最多个数气球的位置,也就是击破三个气球,然后移除这些已经被击破的气球,继续寻找下一个区间,以此循环,直到没有剩余的气球。

代码思路

  1. 初始化两个变量,一个用来统计删除区间的位置,一个用来统计结果

  2. 按照区间的开始位置进行从小到大排序

  3. 从最小的区间开始,查看每一个数字射出箭能够击破的最大气球数以及击破气球的位置

    • 也就是判断某个数字能否被某些区间所包含,即是否大于等于区间的最小值且小于等于区间的最大值

    • 找到最多的能被包含的位置及相关区间,删除这些区间

  4. 从当前集合中取出下一个最小区间重复以上步骤直到集合为空

  5. 返回结果

但是理所当然的报了超时,因为我们尝试去遍历了每一个可能射出箭的位置

image-20240301103046577

2.2 思路二

根据上面的思路,其实我们的本质就是找每个区间和其余区间的最大交集个数。

而因为如果我们按照区间的开始位置从小到大进行排序,那么我们就需要根据下一个区间的开始位置是否小于等于当前遍历区间的结束位置,如下,假设现在遍历第一个蓝色区间:

image-20240301111734803

我们发现绿色的开始位置小于蓝色气球的结束位置,所以我们就可以断定他们俩有交集(因为左端点已经经过排序了的)。然后再看下一个区间也就是黄色气球,发现还是有交集,那么我们是不是就可以断定这一支箭一定就可以一串三呢?

答案是否定的,虽然对于上述图中是可以从x=6射出一只箭来打破这三个气球,然后从剩余气球的第一个位置继续遍历,但是如果是如下的情况呢?

image-20240301112249384

这时可以发现,虽然遍历的第一个蓝色气球和黄色绿色两个气球都相交,但是我们并不能用一只箭将他们全部射破,因为绿色和黄色并不像上面开始的图中给出的示例一样能够在 x=6处相交。

所以此时要注意,在判断完毕第一个蓝色气球和绿色气球之后,我们需要把结束位置更新,更新为 6,因为绿色气球必须要一只 x<=6 的箭来射穿它。

这时如果结束位置是6,我们再来判断黄色气球的开始位置和当前区间也就是 【1,6】是否相交,发现是不可以的,就可以断定需要一只箭来射穿前两个气球。然后我们再从黄色气球开始向后按照之前的步骤继续发出箭。

所以核心点就是: 我们一定要把结束位置更新为走过的气球中的最小值

所以根据以上性质,我们可以给出以下代码思路:

代码思路:

  1. 对气球区间进行升序排序

  2. 遍历每一个区间,查看当前区间的下一个区间的开始位置是否小于等于当前区间的结束位置

    • 如果满足,就将遍历的区间索引++,也就相当于移除了这个区间,然后更新右端点为两者的最小值。

  3. 如果走到某一个区间发现不满足上述条件,那么直接射出一只箭,然后遍历剩下的区间

小错误排查

但是按照以上思路求解发现了一个问题,就是对于该测试用例:

image-20240301115301619

发现解答错误,然后我debug了一下,发现按照从小到大排序是这样的:

image-20240301115243116

通过排查了一番,找到了原因如下:

在排序时,我是用如下代码:

        //1. 对气球区间进行升序排序Arrays.sort(points, (o1, o2) -> o1[0] - o2[0]);

该代码片段是使用一个自定义比较器对二维数组points进行排序的Java代码。这个比较器是一个lambda表达式,它比较两个区间的开始点o1[0]o2[0]

这个比较器通过计算两个开始点的差值o1[0] - o2[0]来决定排序顺序。在Java中,Comparator接口期望的返回值为负数、零或正数,分别表示第一个参数小于、等于或大于第二个参数。

但是因为测试用例中整数值接近Integer类型的极限,所以这个比较器的计算可能会导致整数溢出:

  • 如果o1[0]是一个非常大的正数,而o2[0]是一个负数,那么理论上o1[0]应该大于o2[0]

  • 但是如果o1[0]o2[0]的差值大于Integer.MAX_VALUE(即2147483647),那么计算结果将会溢出,导致返回一个负值。

  • 这会使得排序函数错误地认为o1[0]小于o2[0]

为了避免整数溢出,那么应该使用Integer.compare()方法来比较两个整数,它能正确处理溢出的情况。下面是修改后的代码:

Arrays.sort(points, (o1, o2) -> Integer.compare(o1[0], o2[0]));

使用Integer.compare()方法可以确保即使是接近整数极限的值,比较操作也会正确执行,返回正确的排序顺序。

经过修改后完美解决!

2.3 思路三

在思路二中,实际上是对思路一的优化,减少了一些不必要的判断,思路二需要不断更新右边界来确保正确的射出箭。那么我们可不可以不更新右边界呢?

因为我们之所以不断更新右边界,就是想知道在哪个位置之前必须得射出一只箭,这是必须依赖于右边界的值的。那我们是不是可以根据右边界从小到大进行排序,以每一个当前区间的右边界作为指标,判断其余区间是否能和它相交?

这样其实每一个右边界就是一次极限,必须射出一只箭的极限,因为如果到了右边界还不射出,那么当前这个气球就无法被击破了。所以根据以上思路,我们可以对右边界从小到大排序,得出以下:

代码思路:

假设给定的区间集合中的每个区间表示为一个点对[xstart, xend]

  1. 按照xend排序:首先将所有区间按照xend的值从小到大排序,这样可以确保我们每次选择的区间都是最先结束的。

  2. 选择区间:从排序后的区间列表中选择第一个区间,这个区间的xend将覆盖第一个坐标点。这将是我们选择的第一个区间。

  3. 覆盖剩余的点:然后遍历剩下的点,对于每个点,如果它没有被当前已经选择的区间集合中的任何一个区间覆盖,那么我们就选择下一个区间。我们选择的区间是排序后列表中第一个xstart小于或等于该点坐标的区间。

  4. 重复:重复上述过程,直到所有的点都被覆盖。

  5. 计数:在上述过程中,我们每选择一个区间就将计数器加一,最后的计数器的值就是最少需要的区间数。

3. 代码实现

3.1 思路一

image-20240301103352922

image-20240301103400613

3.2 思路二

image-20240301120054936

image-20240301120103742

3.3 思路三

image-20240301121019506

image-20240301121004056

4. 相关复杂度分析

解法一

  • 时间复杂度:O(n^2),最坏情况下,每个区间都会与每个箭进行比较。

  • 空间复杂度:O(1),没有使用额外的空间,除了几个变量以外。

解法二

  • 时间复杂度:O(n log n),排序的时间复杂度是O(n log n),遍历区间的时间复杂度是O(n),因此总的时间复杂度是O(n log n)。

  • 空间复杂度:O(1),排序可能需要O(log n)的空间复杂度(如果使用的是快速排序),但通常我们认为这是排序操作的内在空间使用,并且不会超过O(log n)。

解法三

  • 时间复杂度:O(n log n),与解法二类似,主要的时间消耗在于排序。

  • 空间复杂度:O(1),同样的理由,主要空间消耗在排序的栈空间上,不会超过O(log n)。

需要注意的是,实际的时间复杂度还取决于使用的排序算法。

在Java中,Arrays.sort()方法对于基本类型使用的是快速排序的变种,对于对象类型使用的是稳定的归并排序,归并排序的空间复杂度是O(n)。由于本题中排序的是int[][]类型,即对象类型,所以如果严格来说,空间复杂度应为O(n)。但在面试或者实际工作中,如果不是特别强调,我们通常认为这是内置的排序函数的空间复杂度,而不将其计入算法的空间复杂度中。

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

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

相关文章

如何使用Portainer创建Nginx容器并搭建web网站发布至公网可访问【内网穿透】

文章目录 前言1. 安装Portainer1.1 访问Portainer Web界面 2. 使用Portainer创建Nginx容器3. 将Web静态站点实现公网访问4. 配置Web站点公网访问地址4.1公网访问Web站点 5. 固定Web静态站点公网地址6. 固定公网地址访问Web静态站点 前言 Portainer是一个开源的Docker轻量级可视…

C# OpenVINO Crack Seg 裂缝分割 裂缝检测

目录 效果 模型信息 项目 代码 数据集 下载 C# OpenVINO Crack Seg 裂缝分割 裂缝检测 效果 模型信息 Model Properties ------------------------- date&#xff1a;2024-02-29T16:35:48.364242 author&#xff1a;Ultralytics task&#xff1a;segment version&…

【生成式AI】ChatGPT原理解析(1/3)- 对ChatGPT的常见误解

Hung-yi Lee 课件整理 文章目录 误解1误解2ChatGPT真正在做的事情-文字接龙 ChatGPT是在2022年12月7日上线的。 当时试用的感觉十分震撼。 误解1 我们想让chatGPT讲个笑话&#xff0c;可能会以为它是在一个笑话的集合里面随机地找一个笑话出来。 我们做一个测试就知道不是这样…

C# Post数据或文件到指定的服务器进行接收

目录 应用场景 实现原理 实现代码 PostAnyWhere类 ashx文件部署 小结 应用场景 不同的接口服务器处理不同的应用&#xff0c;我们会在实际应用中将A服务器的数据提交给B服务器进行数据接收并处理业务。 比如我们想要处理一个OFFICE文件&#xff0c;由用户上传到A服务器…

基于springboot+vue的贸易行业crm系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

Java-nio

一、NIO三大组件 NIO的三大组件分别是Channel&#xff0c;Buffer与Selector Java NIO系统的核心在于&#xff1a;通道(Channel)和缓冲区(Buffer)。通道表示打开到 IO 设备(例如&#xff1a;文件、套接字)的连接。若需要使用 NIO 系统&#xff0c;需要获取用于连接 IO 设备的通…

Windows环境下的调试器探究——硬件断点

与软件断点与内存断点不同&#xff0c;硬件断点不依赖被调试程序&#xff0c;而是依赖于CPU中的调试寄存器。 调试寄存器有7个&#xff0c;分别为Dr0~Dr7。 用户最多能够设置4个硬件断点&#xff0c;这是由于只有Dr0~Dr3用于存储线性地址。 其中&#xff0c;Dr4和Dr5是保留的…

java中容器继承体系

首先上图 源码解析 打开Collection接口源码&#xff0c;能够看到Collection接口是继承了Iterable接口。 public interface Collection<E> extends Iterable<E> { /** * ...... */ } 以下是Iterable接口源码及注释 /** * Implementing this inte…

makefileGDB使用

一、makefile 1、make && makefile makefile带来的好处就是——自动化编译&#xff0c;一旦写好&#xff0c;只需要一个make命令&#xff0c;整个工程完全自动编译&#xff0c;极大的提高了软件开发的效率 下面我们通过如下示例来进一步体会它们的作用&#xff1a; ①…

使用 Python 实现一个飞书/微信记账机器人,酷B了!

Python飞书文档机器人 今天的主题是&#xff1a;使用Python联动飞书文档机器人&#xff0c;实现一个专属的记账助手&#xff0c;这篇文章如果对你帮助极大&#xff0c;欢迎你分享给你的朋友、她、他&#xff0c;一起成长。 也欢迎大家留言&#xff0c;说说自己想看什么主题的…

代码随想录第天 78.子集 90.子集II

LeetCode 78 子集 题目描述 给你一个整数数组 nums &#xff0c;数组中的元素 互不相同 。返回该数组所有可能的子集&#xff08;幂集&#xff09;。 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&…

LeetCode 2581.统计可能的树根数目:换根DP(树形DP)

【LetMeFly】2581.统计可能的树根数目&#xff1a;换根DP(树形DP) 力扣题目链接&#xff1a;https://leetcode.cn/problems/count-number-of-possible-root-nodes/ Alice 有一棵 n 个节点的树&#xff0c;节点编号为 0 到 n - 1 。树用一个长度为 n - 1 的二维整数数组 edges…

【通信基础知识】完整通信系统的流程图及各模块功能详解

2024.2.29 抱歉最近在写毕设大论文&#xff0c;因此没有太多时间更新。然而&#xff0c;在写论文的过程中&#xff0c;发现自己对通信系统的了解还不够全明白&#xff0c;因此差了一些硕博论文总结了一个完整的通信系统流程图。若有不对的地方请多多指正//部分内容有参考ChatGP…

YOLOv7基础 | 第2种方式:简化网络结构之yolov7.yaml(由104层简化为30层)

前言:Hello大家好,我是小哥谈。通过下载YOLOv7源码可知,原始的yolov7.yaml文件是拆开写的,比较混乱,也不好理解,并且为后续改进增添了很多困难。基于此种情况,笔者就给大家介绍一种将yolov7.yaml文件简化的方法,将104层简化为30层,并且参数量和计算量和原来是一致的,…

内存占用构造方法

#使用虚拟内存构造内存消耗 mkdir /tmp/memory mount -t tmpfs -o size5G tmpfs /tmp/memory dd if/dev/zero of/tmp/memory/block #释放消耗的虚拟内存 rm -rf /tmp/memory/block umount /tmp/memory rmdir /tmp/memory #内存占用可直接在/dev/shm目录下写文件

NLP(一)——概述

参考书: 《speech and language processing》《统计自然语言处理》 宗成庆 语言是思维的载体&#xff0c;自然语言处理相比其他信号较为特别 word2vec用到c语言 Question 预训练语言模型和其他模型的区别? 预训练模型是指在大规模数据上进行预训练的模型&#xff0c;通常…

测试环境搭建整套大数据系统(七:集群搭建kafka(2.13)+flink(1.13.6)+dinky(0.6)+iceberg)

一&#xff1a;搭建kafka。 1. 三台机器执行以下命令。 cd /opt wget wget https://dlcdn.apache.org/kafka/3.6.1/kafka_2.13-3.6.1.tgz tar zxvf kafka_2.13-3.6.1.tgz cd kafka_2.13-3.6.1/config vim server.properties修改以下俩内容 1.三台机器分别给予各自的broker_id…

Python+neo4j构建豆瓣电影知识图谱

文章目录 数据来源数据整理导入节点和关系导入使用Subgraph批量导入节点和关系 多标签实体和实体去重 数据来源 http://www.openkg.cn/dataset/douban-movie-kg 该网址拥有丰富的中文知识图谱数据集&#xff0c;OpenKG(Open Knowledge Graph)&#xff0c;可供研究人员使用研究…

【golang】25、图片操作

用 “github.com/fogleman/gg” 可以画线, 框 用 “github.com/disintegration/imaging” 可以变换颜色 一、渲染 1.1 框和字 import "github.com/fogleman/gg"func DrawRectangles(inPath string, cRects []ColorTextRect, fnImgNameChange FnImgNameChange) (st…

Python爬虫——Urllib库-3

目录 ajax的get请求 获取豆瓣电影第一页的数据并保存到本地 获取豆瓣电影前十页的数据 ajax的post请求 总结 ajax的get请求 获取豆瓣电影第一页的数据并保存到本地 首先可以在浏览器找到发送数据的接口 那么我们的url就可以在header中找到了 再加上UA这个header 进行请…