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

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

[此处请插入:排序后的气球区间与贪心射箭重叠示意图]

✨核心逻辑

本题采用 贪心算法 的策略:

  1. 按右边界排序:将所有气球的区间按照 右边界(结束坐标) 升序排列。这样可以保证每次射出的箭,都能尽可能靠右,从而最大可能地覆盖后续重叠的气球。
  2. 初始化弓箭:第一支箭一定需要射出,将其射击位置定为第一个气球的右边界 index = points[0][1]
  3. 贪心判断与更新
    • 因为数组按右边界升序,后面气球的右边界一定大于等于当前 index
    • 如果当前气球的 左边界 points[i][0] 大于 当前弓箭的射击位置 index,说明该气球超出了这支箭的射程,无法被引爆。此时必须新增一支弓箭,并把箭的射击位置更新为这个新气球的右边界。
    • 如果左边界 小于等于 当前弓箭位置,说明在这个区间内不用新增弓箭,它被上一支箭覆盖了。

🔥代码实现(含详细变量注释)

class Solution {
    public int findMinArrowShots(int[][] points) {
        // 贪心!边界情况:如果没有气球,直接返回 0
        if (points.length == 0) return 0;
        
        // 1. 根据气球的右边界(结束坐标)进行升序排序
        // 使用 Integer.compare 规避减法溢出问题,这是一种更严谨的写法
        Arrays.sort(points, (a, b) -> Integer.compare(a[1], b[1]));
        
        // length:记录气球的个数
        int length = points.length;
        
        // num:记录最少需要射出的弓箭数量。因为一定存在气球,所以初始化为 1
        int num = 1;
        
        // index:记录当前弓箭的引爆位置(上一个已处理气球的右边界),初始为第一个气球的右边界
        int index = points[0][1];
        
        // i:遍历指针,从第二个气球开始,依次往后判断
        for (int i = 1; i < length; i++) {
            // 判断当前气球的左边界是否大于弓箭的当前位置
            // 如果是,说明前面的箭无法引爆这个气球,必须新射出一支箭
            if (points[i][0] > index) {
                num++;              // 需要的弓箭数量 +1
                index = points[i][1]; // 将新弓箭的引爆位置更新为当前气球的右边界
            }
            // 否则,当前气球与前面的重叠,会被同一支箭引爆,不做任何操作
        }
        return num;
    }
}
  • ⏱️复杂度分析
    • 时间复杂度O(N log N),其中 N 是气球的个数。主要的时间开销在于使用 Arrays.sort 进行排序,复杂度为 O(N log N);随后的线性遍历仅需 O(N),综合时间复杂度为 O(N log N)。

    • 空间复杂度:O(log N) 或 O(1)(取决于排序算法的实现)。Java 的 Arrays.sort 底层基于双轴快排,需要占用递归栈空间,大致为 O(log N)。如果不考虑排序的栈开销,仅额外使用了 length、num、index、i 等常数个变量,空间消耗为 O(1)。

🔍总结一下