leetcode
leetcode 401 ~ 450
用最少数量的箭引爆气球

用最少数量的箭引爆气球

难度:

标签:

题目描述

有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points ,其中points[i] = [xstart, xend] 表示水平直径在 xstart 和 xend之间的气球。你不知道气球的确切 y 坐标。

一支弓箭可以沿着 x 轴从不同点 完全垂直 地射出。在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstartxend 且满足  xstart ≤ x ≤ xend则该气球会被 引爆 可以射出的弓箭的数量 没有限制 。 弓箭一旦被射出之后,可以无限地前进。

给你一个数组 points返回引爆所有气球所必须射出的 最小 弓箭数 

 

示例 1:

输入:points = [[10,16],[2,8],[1,6],[7,12]]
输出:2
解释:气球可以用2支箭来爆破:
-在x = 6处射出箭,击破气球[2,8]和[1,6]。
-在x = 11处发射箭,击破气球[10,16]和[7,12]。

示例 2:

输入:points = [[1,2],[3,4],[5,6],[7,8]]
输出:4
解释:每个气球需要射出一支箭,总共需要4支箭。

示例 3:

输入:points = [[1,2],[2,3],[3,4],[4,5]]
输出:2
解释:气球可以用2支箭来爆破:
- 在x = 2处发射箭,击破气球[1,2]和[2,3]。
- 在x = 4处射出箭,击破气球[3,4]和[4,5]。

 

提示:

  • 1 <= points.length <= 105
  • points[i].length == 2
  • -231 <= xstart < xend <= 231 - 1

代码结果

运行时间: 116 ms, 内存: 48.4 MB


/*
题目思路:
同上,在 Java Stream 中,我们可以使用 stream 来对数组进行排序,并利用 reduce 函数来计算最少的箭数。对于每个气球,我们检查当前气球的开始位置是否大于之前的箭的结束位置,如果是则增加箭的数量,并更新箭的结束位置。
*/
import java.util.Arrays;
 
public class Solution {
    public int findMinArrowShots(int[][] points) {
        return Arrays.stream(points)
                // 按结束位置排序
                .sorted((a, b) -> Integer.compare(a[1], b[1]))
                // 使用 reduce 来计算最小箭数
                .reduce(new int[]{0, Integer.MIN_VALUE}, (result, point) -> {
                    // 如果当前气球的开始位置大于箭的结束位置
                    if (point[0] > result[1]) {
                        result[0]++; // 增加箭的数量
                        result[1] = point[1]; // 更新箭的结束位置
                    }
                    return result;
                }, (a, b) -> a)[0]; // 返回箭的数量
    }
}

解释

方法:

这个题解的思路是先按照气球结束坐标从小到大排序,然后从前往后遍历气球,用一支箭尽可能多地引爆气球。具体来说,初始化一个 pos 变量表示上一支箭的引爆位置,初始化为第一个气球的结束坐标。从第二个气球开始遍历,如果当前气球的开始坐标大于 pos,说明之前的箭无法引爆当前气球,需要增加一支箭,并更新 pos 为当前气球的结束坐标。遍历结束后,ans 即为最少需要的弓箭数。

时间复杂度:

O(nlogn)

空间复杂度:

O(1)

代码细节讲解

🦆
为什么在解决这个问题时选择按气球的结束坐标进行排序而不是开始坐标?
选择按气球的结束坐标进行排序是为了确保每次射箭时能尽可能多地覆盖后续的气球。这种排序方式可以帮助我们确定一个最迟的射箭点,即当前箭所能达到的最远气球的结束位置。这样可以保证在不失去引爆任何气球的前提下,用最少的箭覆盖最多的气球。如果按照开始坐标排序,我们可能会过早地放置箭,从而导致需要更多的箭来覆盖后面开始坐标较晚但结束坐标较早的气球。
🦆
排序气球的结束坐标有什么特别的好处,能否解释其在算法中的作用?
排序气球的结束坐标的好处在于,它帮助我们确定射箭的最优位置。这种排序方式允许我们从当前射箭位置尽可能多地向后覆盖气球。在算法中,这种排序方法简化了决策过程:只要下一个气球的开始坐标在当前箭位置之后,就意味着需要新的箭。这样可以确保每次放置箭时,都是在目前可能的最迟点,从而尽可能地减少总箭数。
🦆
在算法中,如果存在多个气球具有相同的结束坐标,这种情况如何处理?会影响箭的数量吗?
如果多个气球具有相同的结束坐标,它们可以被一支箭同时引爆。在算法中,这种情况下,这些气球的开始坐标可能不同,但只要有一个气球的开始坐标小于或等于当前的射箭位置(pos),所有这些气球都将被当前的箭覆盖。因此,这种情况不会增加所需的箭的数量,反而有助于减少总箭数,因为一箭可以同时引爆多个气球。
🦆
题解中提到的变量`pos`的更新策略为什么是在需要新箭时才更新?是否有其他策略可以减少箭的数量?
变量`pos`的更新策略是在确定必须引入新箭时才进行更新,因为这保证了每次更新都是必要的,并且每次更新都能尽可能多地覆盖未来的气球。此策略确保了每次放置的箭都是在当前可用的最迟射箭位置,从而尽可能减少总箭数。实际上,这已经是一个非常有效的策略,因为它是基于当前已知的最优解放置箭。其他策略,如频繁更新pos或在不同的条件下更新pos,通常不会减少箭的总数,反而可能增加复杂性和箭的数量。

相关问题

会议室 II

会议室 II

无重叠区间

给定一个区间的集合 intervals ,其中 intervals[i] = [starti, endi] 。返回 需要移除区间的最小数量,使剩余区间互不重叠 

 

示例 1:

输入: intervals = [[1,2],[2,3],[3,4],[1,3]]
输出: 1
解释: 移除 [1,3] 后,剩下的区间没有重叠。

示例 2:

输入: intervals = [ [1,2], [1,2], [1,2] ]
输出: 2
解释: 你需要移除两个 [1,2] 来使剩下的区间没有重叠。

示例 3:

输入: intervals = [ [1,2], [2,3] ]
输出: 0
解释: 你不需要移除任何区间,因为它们已经是无重叠的了。

 

提示:

  • 1 <= intervals.length <= 105
  • intervals[i].length == 2
  • -5 * 104 <= starti < endi <= 5 * 104