区间问题

区间问题

Ⅰ 解题框架

​ 所谓区间问题,就是线段问题,让你合并所有线段、找出线段的交集等等。主要有两个技巧:
1、排序。常见的排序方法就是按照区间起点排序,或者先按照起点升序排序若起点相同,则按照终点降序排序
2、画图。就是说不要偷懒,勤动手,两个区间的相对位置到底有几种可能,不同的相对位置我们的代码应该怎么去处理。

Ⅱ 相关习题

1、删除覆盖区间(LeetCode 1288)

​ 给你一个区间列表,请你删除列表中被其他区间所覆盖的区间。只有当 c <= a 且 b <= d 时,我们才认为区间 [a,b) 被区间 [c,d) 覆盖。在完成所有删除操作后,请你返回列表中剩余区间的数目。

​ 首先排序:按照起点升序排序,起点相同则按照终点降序排序,使用lamda表达式

//将intervals按照起点升序排序,起点相同则按照终点降序排序
        Arrays.sort(intervals,(a,b)->{
            if (a[0] == b[0]) return b[1]-a[1];
            return a[0]-b[0];
        });
image-20201213135848220

排序之后,两个相邻区间只可能有如下三种相对位置:

image-20201213135923221

​ 对于这三种情况,我们应该这样处理:对于情况一,找到了覆盖区间。对于情况二,两个区间可以合并,成一个大区间。对于情况三,两个区间完全不相交。

class Solution {
    public int removeCoveredIntervals(int[][] intervals) {
        int num = intervals.length;
        //将intervals按照起点升序排序,起点相同则按照终点降序排序
        Arrays.sort(intervals,(a,b)->{
            if (a[0] == b[0]) return b[1]-a[1];
            return a[0]-b[0];
        });
        //记录线段的左右边界,初始值就是第一条线段的起点和终点
        int left = intervals[0][0];
        int right = intervals[0][1];

        //开始覆盖
        for (int i=1;i<intervals.length;i++) {
            if (intervals[i][1] <= right) { //区间被覆盖了,对应情况1
                num--;
            }else if (intervals[i][0] >= right) {//对应情况3
                left = intervals[i][0];
                right = intervals[i][1];
            }else if (right>intervals[i][0] && right<intervals[i][1]) {
                //对应情况2
                right = intervals[i][1];
            }
        }
        return num;
    }
}

2、区间合并(LeetCode 56)

​ 和第一题类似,但要注意三种情况的符号,弄清楚到底要不要加等于号

class Solution {
    public int[][] merge(int[][] intervals) {
        
        int len = intervals.length;
        List<int[]> res = new ArrayList<>();
        //排序
        Arrays.sort(intervals,(a,b)-> {
            return a[0] == b[0]?(b[1]-a[1]):(a[0]-b[0]);
        });

        int left = intervals[0][0];
        int right = intervals[0][1];
        res.add(intervals[0]);

        for (int i=1;i<len;i++) {
            if (right >= intervals[i][1]) {
                continue;
            }else if(right >= intervals[i][0] && right < intervals[i][1]) {
                right = intervals[i][1];
                //先取出修改后再重新添加
                int[] tmp = res.remove(res.size()-1);
                tmp[1] = right;
                res.add(tmp);
            } else if (right < intervals[i][0]) { //注意此处为严格小于号,不能写小于等于!
                left = intervals[i][0];
                right = intervals[i][1];
                int[] tmp2 = {left,right};
                res.add(tmp2);
            }
        }
        return res.toArray(new int[res.size()][]);
    }
}
image-20201213143517525

3、区间交集问题(LeetCode 986)

​ 给定两个由一些 闭区间 组成的列表,每个区间列表都是成对不相交的,并且已经排序。返回这两个区间列表的交集。形式上,闭区间 [a, b](其中 a <= b)表示实数 x 的集合,而 a <= x <= b。两个闭区间的交集是一组实数,要么为空集,要么为闭区间。例如,[1, 3] 和 [2, 4] 的交集为 [2, 3]。)

此题注意简化,不然情况太多

class Solution {
    public int[][] intervalIntersection(int[][] A, int[][] B) {
        int lenA = A.length;int lenB = B.length;
        if (lenA == 0 || lenB == 0) return new int[0][0];

        int indexA = 0;int indexB = 0;
        List<int[]> res = new ArrayList<>();
        while (indexA < lenA && indexB <lenB) {
            //可以这样简化!!!
            int lo = Math.max(A[indexA][0],B[indexB][0]);
            int hi = Math.min(A[indexA][1],B[indexB][1]);

            if (lo<=hi) {
                res.add(new int[]{lo,hi});
            }
            //谁的右边小,谁的指针就右移
            if (A[indexA][1]>=B[indexB][1]) {
                indexB++;
            }else {
                indexA++;
            }
        }
        return res.toArray(new int[res.size()][]);
    }
}

区间贪心算法

4、无重叠区间(LeetCode 435)

给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。

注意:可以认为区间的终点总是大于它的起点。区间 [1,2] 和 [2,3] 的边界相互“接触”,但没有相互重叠。

image
class Solution {
    public int eraseOverlapIntervals(int[][] intervals) {

        if (intervals.length == 0) return 0;
        return intervals.length-interval(intervals);
    }

    int interval (int[][] intervals) {
        int res = 1;
        Arrays.sort(intervals,(a,b)->{
            return a[1]-b[1];
        }); 
        int start = intervals[0][0];
        int end = intervals[0][1];
        for (int i=1;i<intervals.length;i++) {
            if (end <= intervals[i][0]) {
                res++;
                start = intervals[i][0];
                end = intervals[i][1];
            }
        }
        return res;
    }
}

5、用箭引爆气球(LeetCode 452)

与上一题不同的是两个区间交集为一个点时还是算作一个区间

class Solution {
    public int findMinArrowShots(int[][] points) {

        if (points.length == 0) return 0;
        Arrays.sort(points,(a,b)->{
            //不要用return a[1]-b[1];  会发生溢出
            return a[1] > b[1] ? 1 : -1;
        });
        int res = 1;
        int start = points[0][0];
        int end = points[0][1];
        for (int i=1;i<points.length;i++) {
            if (end < points[i][0]) {
                res++;
                start = points[i][0];
                end = points[i][1];
            }
        }
        return res;
    }
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 区间覆盖 这道题的本质——让我们找到给定区间中最能代表线段区间的区间,然后尽可能少。目的是可以完全代表这个线段区间...
    StevenHD阅读 117评论 0 0
  • 区间分组 目标是给给定的所有区间分组,然后每一个组中的所有区间都没有交集,然后尽可能的组数要少一些。 核心——找出...
    StevenHD阅读 162评论 0 0
  • 本文对区间查询问题常用的数据结构方法进行总结 1. 前缀和 前缀和是降低区间查询问题复杂度的一种常见预处理方法,对...
    舒也ella阅读 2,385评论 0 0
  • 一、题目 我们来对比分析2个问题。 区间选点 最大不相交区间数量 先来看下题意—— 其实这两道题是一样的。代码都是...
    StevenHD阅读 432评论 0 0
  • 读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目: 1288.删除被覆盖区间[htt...
    labuladong阅读 408评论 0 5