力扣452题、435题、56题(用最少量的箭引爆气球,无重叠区间,合并区间)

452、用最少量的箭引爆气球

基本思想:

贪心

具体实现:

局部最优:当气球出现重叠,射,所用弓箭最少

全局最优:把所有气球射爆

1.为了让气球更好的重叠,需要对气球数组进行排序

   按照起始位置进行排序,从前向后遍历数组,靠左尽可能让气球重复

2.气球重叠后,重叠气球中右边边界的最小值之前的区间需要一个弓箭

 

3.气球1,2需要一个弓箭,

气球3的左边界大于了第一组重叠气球的最小右边界,所以气球3需要另一支箭

4.题目中说满足 xstart ≤ x ≤ xend,则该气球会被引爆。

说明两个气球挨在一起不重叠也可以一起射爆,所以代码中 if (points[i][0] > points[i - 1][1]) 不能是>=

 

代码:

class Solution {
    public int findMinArrowShots(int[][] points) {
        if (points.length == 0) return 0;
        Arrays.sort(points, (o1, o2) -> Integer.compare(o1[0],o2[0]));
        int count = 1;//不为空至少需要一支箭
        for (int i = 1; i < points.length; i++){
            if (points[i][0] > points[i-1][1]){//后一个气球的左边和前一个气球的右边比,就可以看出是否重合,这里是不重合,需要一支箭
                count++;

            }else{//重合的话,更新重叠气球的最小右边界,直到下一次不重合的时候才需要一支箭
                points[i][1] = Math.min(points[i][1], points[i-1][1]);
            }

        }
        return count;
    }
}

 

435、无重叠区间

基本思想:

贪心算法

具体实现:

1.按照右边界排序,就从左向右遍历

2.从左向右记录非交叉区间的个数

3.区间总数 - 非交叉区间的个数 = 要移除区间的个数

局部最优:因为按右边界排序的,从左向右遍历的话可以优先选右边界小的区间,

                      留给下一个区间的空间大一些,可以更容易避免交叉

全局最优:选取最多的非交叉区间

 

1. 区间1,2,3,4,5,6按照右边界排好序

每次找没有交叉的区间是,用右边界最小的来找,这样留给下一个区间的空间就越大,

第一条分割线就是区间1结束的位置

2.再找和区间1没有交叉的区间,选择区间4

    区间5和区间1也没有交叉,但是区间4的右边界小于区间5,所以选择区间4,而且是按右边界小的顺序来遍历的

3.接下来找到区间6

4.有3个没有交叉的区间

5.总区间-没有交叉的区间

 

  • 难点一:一看题就有感觉需要排序,但究竟怎么排序,按左边界排还是右边界排。
  • 难点二:排完序之后如何遍历,如果没有分析好遍历顺序,那么排序就没有意义了。
  • 难点三:直接求重复的区间是复杂的,转而求最大非重复区间个数。
  • 难点四:求最大非重复区间个数时,需要一个分割点来做标记。

代码:

class Solution {
    public int eraseOverlapIntervals(int[][] intervals) {
        if (intervals.length < 2) return 0;
        Arrays.sort(intervals, new Comparator<int[]>(){
            public int compare(int[] o1, int[] o2){
                if (o1[1] != o2[1]){
                    return Integer.compare(o1[1], o2[1]);
                } else {
                    return Integer.compare(o1[0], o2[0]);
                }
            }
        });

        int count = 1;
        int edge = intervals[0][1];
        for (int i = 1; i < intervals.length; i++){
            if (edge <= intervals[i][0]){
                count++;
                edge = intervals[i][1];
            }
        }
        return intervals.length - count;
    }
}

 

56、合并区间

基本思想:

贪心

具体实现:

1.按照左边界排序

2.局部最优:每次合并取最大的右边界,这样可以合并更多的区间

3.整体最优:合并所有重叠的区间

代码:

class Solution {
    public int[][] merge(int[][] intervals) {
        List<int[]> res = new LinkedList<>();
        Arrays.sort(intervals, (o1, o2) -> Integer.compare(o1[0], o2[0]));

        int start = intervals[0][0];
        for (int i = 1; i < intervals.length; i++){
            //如果当前左边界大于上一个右边界,说明没有重合
            if (intervals[i][0] > intervals[i-1][1]) {
                //就把靠左的这个和别人没重合的区间加入结果数组
                res.add(new int[]{start, intervals[i - 1][1]});
                //更新start为靠右的这个区间的左边界
                start = intervals[i][0];
            //这两个区间重合了 
            } else {
                //取当前区间右边界和上一个区间的右边界的最大值
                intervals[i][1] = Math.max(intervals[i][1], intervals[i-1][1]);
            }
        }
        res.add(new int[]{start, intervals[intervals.length - 1][1]});
        return res.toArray(new int[res.size()][]);
    }
}

 

posted @ 2021-10-23 21:37  最近饭吃的很多  阅读(46)  评论(0编辑  收藏  举报