12贪心:用最少数量的箭引爆气球

12贪心:用最少数量的箭引爆气球

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

如何使用最少的弓箭呢?

直觉上来看,貌似只射重叠最多的气球,用的弓箭一定最少,那么有没有当前重叠了三个气球,我射两个,留下一个和后面的一起射这样弓箭用的更少的情况呢?

尝试一下举反例,发现没有这种情况。

那么就试一试贪心吧!局部最优:当气球出现重叠,一起射,所用弓箭最少。全局最优:把所有气球射爆所用弓箭最少。

为了让气球尽可能的重叠,需要对数组进行排序

那么按照气球起始位置排序,还是按照气球终止位置排序呢?

其实都可以!只不过对应的遍历顺序不同,我就按照气球的起始位置排序了。

既然按照起始位置排序,那么就从前向后遍历气球数组,靠左尽可能让气球重复。

从前向后遍历遇到重叠的气球了怎么办?

如果气球重叠了,重叠气球中右边边界的最小值 之前的区间一定需要一个弓箭

class Solution {
    public int findMinArrowShots(int[][] points) {
//一发箭矢要尽可能穿过更多的气球 箭矢最少
    //转换成如何能判断是最多的
    //按照起始位置排序,当遇到末尾>=最开始的头,就可以射击了
    int end = 0;
    int cnt = 1;
    
    // Arrays.sort(points, (a, b) -> {
    //     return a[0] - b[0];
    // });遇到大数这种排序方法不行[[-2147483646,-2147483645],[2147483646,2147483647]]
    Arrays.sort(points, (a, b) -> Integer.compare(a[0], b[0]));
    end = points[0][1];
    int start = points[0][0];

    for(int[] p : points) {
        if(p[0] > end) {//
            cnt++;//用掉一支箭
            end = p[1];//更新末尾边界
        } else {
            end = Math.min(end, p[1]);
        }
    }

    return cnt;
    }
}
/*
[[3,9],[7,12],[3,8],[6,8],[9,10],[2,9],[3,9],[2,8]]
0,6 0,9
*/
class Solution {
    public int findMinArrowShots(int[][] points) {
//一发箭矢要尽可能穿过更多的气球 箭矢最少
    //转换成如何能判断是最多的
    //按照起始位置排序,当遇到末尾>=最开始的头,就可以射击了
    int cnt = 1;
    
    // Arrays.sort(points, (a, b) -> {
    //     return a[0] - b[0];
    // });遇到大数这种排序方法不行[[-2147483646,-2147483645],[2147483646,2147483647]]
    Arrays.sort(points, (a, b) -> Integer.compare(a[0], b[0]));
    int end = points[0][1];//上一个气球的末尾

    for(int i = 1; i < points.length; i++) {
        if(end < points[i][0]) {//相邻两个气球不重合
            cnt++;//用掉一支箭
            end = points[i][1];
        } else {//重合
            end = Math.min(end, points[i][1]);
        }
    }

    return cnt;
    }
}
/**
 * 时间复杂度 : O(NlogN)  排序需要 O(NlogN) 的复杂度
 * 空间复杂度 : O(logN) java所使用的内置函数用的是快速排序需要 logN 的空间
 */
class Solution {//同样的思路,优秀代码
    public int findMinArrowShots(int[][] points) {
        // 根据气球直径的开始坐标从小到大排序
        // 使用Integer内置比较方法,不会溢出
        Arrays.sort(points, (a, b) -> Integer.compare(a[0], b[0]));

        int count = 1;  // points 不为空至少需要一支箭
        for (int i = 1; i < points.length; i++) {
            if (points[i][0] > points[i - 1][1]) {  // 气球i和气球i-1不挨着,注意这里不是>=
                count++; // 需要一支箭
            } else {  // 气球i和气球i-1挨着
                points[i][1] = Math.min(points[i][1], points[i - 1][1]); // 更新重叠气球最小右边界
            }
        }
        return count;
    }
}

这道题目贪心的思路很简单也很直接,就是重复的一起射了,但本题我认为是有难度的。

就算思路都想好了,模拟射气球的过程,很多同学真的要去模拟了,实时把气球从数组中移走,这么写的话就复杂了。

而且寻找重复的气球,寻找重叠气球最小右边界,其实都有代码技巧。

贪心题目有时候就是这样,看起来很简单,思路很直接,但是一写代码就感觉贼复杂无从下手。

这里其实是需要代码功底的,那代码功底怎么练?

多看多写多总结!

你可能感兴趣的:(算法刷题笔记,算法,贪心算法)