面试经典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轻量级可视…

SQL 常见命令及规范

常见命令 1. 查看当前所有数据库 show databases; 2. 打开指定的库 use 库名 ; 3. 查看当前库的所有表 show tables; 4. 查看其他库的所有表 show tables from 库名 ; 5. 创建表 cerate table 表名 ( 列名 列类型&#xff0c; 列名 列类型&#xff0c; ..... …

基于YOLO家族最新模型YOLOv9开发构建自己的个性化目标检测系统从零构建模型完整训练、推理计算超详细教程【以自建数据酸枣病虫害检测为例】

在我前面的系列博文中,对于目标检测系列的任务写了很多超详细的教程,目的是能够读完文章即可实现自己完整地去开发构建自己的目标检测系统,感兴趣的话可以自行移步阅读: 《基于官方YOLOv4-u5【yolov5风格实现】开发构建目标检测模型超详细实战教程【以自建缺陷检测数据集为…

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&…

去掉WordPress网页图片默认链接功能

既然是wordpress自动添加的&#xff0c;那么我们在上传图片到wordpress后台多媒体的时候&#xff0c;就可以手动改变链接指向或者删除掉&#xff0c;问题是每次都要这么做很麻烦&#xff0c;更别说有忘记的时候。一次性解决这个问题有两种方法&#xff0c;一种是No Image Link插…

【生成式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服务器…

中国汽车电子行业发展现状分析及投资前景预测报告

全版价格&#xff1a;壹捌零零 报告版本&#xff1a;下单后会更新至最新版本 交货时间&#xff1a;1-2天 第一章 汽车电子相关概述 1.1 汽车的相关介绍 1.1.1 汽车的概念 我国国家最新标准《汽车和挂车类型的术语和定义》&#xff08;GB/T3730&#xff0e;1—2001&…

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

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

Flink分区相关

0、要点 Flink的分区列不会存数据&#xff0c;也就是两个列有一个分区列&#xff0c;则文件只会存另一个列的数据 1、CreateTable 根据SQL的执行流程&#xff0c;进入TableEnvironmentImpl.executeInternal&#xff0c;createTable分支 } else if (operation instanceof Crea…

Java-nio

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

Spring的简单使用及内部实现原理

在现代的Java应用程序开发中&#xff0c;Spring Framework已经成为了不可或缺的工具之一。它提供了一种轻量级的、基于Java的解决方案&#xff0c;用于构建企业级应用程序和服务。本文将介绍Spring的简单使用方法&#xff0c;并深入探讨其内部实现原理。 首先&#xff0c;让我们…

mysql8.0使用MGR实现高可用

一、三节点MGR集群的安装部署 1. 安装准备 准备好下面三台服务器&#xff1a; IP端口角色192.168.150.213306mgr1192.168.150.223306mgr2192.168.150.233306mgr3 配置hosts解析 # cat >> /etc/hosts << EOF 192.168.150.21 mgr1 192.168.150.22 mgr2 192.168…

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…

debian/ubuntu 编译安装nginx php

debian/ubuntu 编译安装nginx php tar -zxvf nginx-1.9.9.tar.gz apt-get install libpcre3 libpcre3-dev ./configure --prefix/work/nginx-1.9.9 --with-pcre make make install service iptables stop #关闭防火墙, 可能不需要 修改nginx运行用户为tboqi 抱着log目录可…