Skip to content

用最少数量的箭引爆气球

约 1524 字大约 5 分钟

按起点升序排序按终点降序排序

2025-02-27

452. 用最少数量的箭引爆气球

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

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

给你一个数组 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 <= 10^5
  • points[i].length == 2
  • -2^31 <= xstart < xend <= 2^31 - 1

按终点升序排序

类似合并区间。本质是找最大的不重叠的区间个数。这就是所需要的最少的箭数。

不能使用lambda表达式进行排序,因为i[0] - j[0]会出现越界

输入

points = [[-2147483646,-2147483645],[2147483646,2147483647]]

输出

1

预期结果

2
class Solution {
    public int findMinArrowShots(int[][] points) {
        // 如果没有气球,返回0,因为不需要任何箭
        if (points.length == 0) {
            return 0;
        }

        // 按照每个气球的结束位置进行升序排序
        // 也可使用lambda表达式,按 end 升序排序,使用 Long.compare 避免整数溢出,不加long会溢出
        // Arrays.sort(intvs, new Comparator<int[]>() {
            // public int compare(int[] a, int[] b) {
                // return Long.compare((long)a[1], (long)b[1]);
            // }
        // });
        // 结束的越早,后面的空间就越大,从而后面取到最多区间的可能性就越大。
        Arrays.sort(points, new Comparator<int[]>() {
            public int compare(int[] point1, int[] point2) {
                if (point1[1] > point2[1]) {
                    return 1; // 如果point1的结束位置大于point2的结束位置,返回1
                } else if (point1[1] < point2[1]) {
                    return -1; // 如果point1的结束位置小于point2的结束位置,返回-1
                } else {
                    return 0; // 如果两个结束位置相等,返回0
                }
            }
        });

        // 初始化箭的位置为第一个气球的结束位置
        int pos = points[0][1];
        // 至少需要一支箭
        int ans = 1;

        // 遍历所有的气球
        for (int[] balloon : points) {
            // 如果当前气球的起始位置大于箭的位置,说明当前箭不能射穿这个气球
            if (balloon[0] > pos) {
                // 更新箭的位置为当前气球的结束位置。不需要像第一种方法那样给pos取最小值,因为pos是按照终点升序排序的
                pos = balloon[1];
                // 需要增加一支箭
                ++ans;
            }
        }

        // 返回需要的最少箭数
        return ans;
    }
}
  • 时间复杂度: O(nlogn)O(n \log n) ,其中 nn 是数组 points 的长度。排序的时间复杂度为 O(nlogn)O(n \log n) , 对所有气球进行遍历并计算答案的时间复杂度为 O(n)O(n) ,其在渐进意义下小于前者,因此可以忽略。
  • 空间复杂度: O(logn)O(\log n),即为排序需要使用的栈空间。

按起点升序排序

class Solution {
    public int findMinArrowShots(int[][] points) {
        // 如果没有气球,返回0,因为不需要任何箭
        if (points.length == 0) {
            return 0;
        }

        // 按照每个气球的起始位置进行升序排序,这样只要当前的区间的起点大于上一个区间的终点,后面的区间就都大于。
        // 这里不能使用lambda表达式进行排序,因为`i[0] - j[0]`会出现越界问题,即数特别小。 
        // 解决方案:1.使用匿名内部类,重写compare方法 2.将points转为long类型
        // Arrays.sort(points, (i, j) -> (i[0] - j[0]));
        Arrays.sort(points, new Comparator<int[]>() {
            public int compare(int[] point1, int[] point2) {
                if (point1[0] > point2[0]) {
                    return 1; // 如果point1的起始位置大于point2的起始位置,返回1
                } else if (point1[0] < point2[0]) {
                    return -1; // 如果point1的起始位置小于point2的起始位置,返回-1
                } else {
                    return 0; // 如果两个起始位置相等,返回0
                }
            }
        });

        // 初始化箭的位置为第一个气球的结束位置
        int pos = points[0][1];
        // 至少需要一支箭
        int ans = 1;

        // 遍历所有的气球
        for (int i = 1; i < points.length; i++) {
            // 如果当前气球的起始位置小于等于上一支箭的位置,说明可以用同一支箭射穿。
            // 注意是和上一支箭的位置比较,不是与上一个气球的结束位置比较。重点。
            if (points[i][0] <= pos) {
                // 注意,这里要更新箭的位置为当前气球结束位置与当前箭位置的最小值,
                // 因为第二个区间有可能完全被第一个区间盖住,所以要取终点的最小值,把箭的活动范围限制在最小的终点前。
                pos = Math.min(pos, points[i][1]);
            } else {
                // 如果当前气球的起始位置大于箭的位置,说明当前箭不能射穿这个气球
                pos = points[i][1]; // 更新箭的位置为当前气球的结束位置
                ++ans; // 需要增加一支箭
            }
        }

        // 返回需要的最少箭数
        return ans;
    }
}
  • 时间复杂度O(nlogn)O(n\log n),排序需要O(nlogn)O(n\log n),for循环需要O(n)。
  • 空间复杂度O(logn)O(\log n),即排序需要的栈空间。