Interval(区间类问题)

本文通过一系列示例详细讲解了如何高效处理区间问题,包括区间合并、插入和交集计算。首先介绍了一种排序和栈结合的方法来合并区间,接着展示了在已有区间中插入新区间的方法,最后探讨了两种计算两个区间交集的算法,包括暴力求解和双指针优化。通过这些实例,读者可以深入理解区间操作的常见解题思路。
摘要由CSDN通过智能技术生成

这类问题其实是有一些固定的解题思路,可以节省很多时间,下面以一个简单的例子作为入门(传送门):

leetcode56

先给出代码,最后我们再进行解释:

class Solution {
    public int[][] merge(int[][] intervals) {
        // step 1: sort
        int len = intervals.length;
        if(len < 2) return intervals;
        Arrays.sort(intervals, (a, b)-> a[0] == b[0] ?a[1]-b[1]:a[0]-b[0]);
        Deque<int[]> deque = new LinkedList<>();
        deque.push(intervals[0]);
        int size = 1;
        // step 2: analyze
        for(int i = 1; i<len; i++){
            if(intervals[i][0] > deque.peek()[1]){ // 不能合并
                deque.push(intervals[i]);
                size ++;
            }else{  // 可以合并
                int[] temp = deque.pop();
                temp[1] = Math.max(temp[1], intervals[i][1]);
                deque.push(temp);
            }
        }
        int[][] res = new int[size][2];
        for(int i = 0; i<size; i++){
            res[i][0] = deque.peekLast()[0];
            res[i][1] = deque.pollLast()[1];
        }
        return res;
    }
}

对付这类问题,拿到手,先无脑排序就行了,这里我们需要根据题目要求确定是按照第数组一个元素排还是第二个元素,需要熟练掌握排序规则的书写,建议用lambda,比较清晰整洁。这里我们需要合并区间,最重要的是抓住当前数组的第二个元素,与下个数组的第一元素的比较关系,假设intervals[i][1] >= intervals[i+1][0] 证明是可以合并的,那我们其实就可以修改合并区间的右区间temp[1] = Math.max(temp[1], intervals[i][1]),否则是不能合并的,那就放入结果集中。


再来看一道区间问题的进阶传送门

插入区间hard

这道题拿到手,把上面的那道题解改改,就能AC了,😂,不过的话,大概效果如下,内存大概是O(N)表现效果竟然有超过95%

先上代码:

class Solution {
    public int[][] insert(int[][] intervals, int[] newInterval) {
        int len = intervals.length;
        List<int[]> lists = new ArrayList<>();
        int index = 0;
        for(int i = 0; i<len; i++){
            if(intervals[i][1] < newInterval[0]){
                lists.add(intervals[i]);
                index++;
            }else if(intervals[i][0] > newInterval[1]){
                lists.add(intervals[i]);
            }else{
                newInterval[0] = Math.min(newInterval[0], intervals[i][0]);
                newInterval[1] = Math.max(newInterval[1], intervals[i][1]);
            }
        }
        lists.add(index,newInterval);
        int[][] res = new int[lists.size()][2];
        for(int i = 0; i<lists.size(); i++){
            res[i][0] = lists.get(i)[0];
            res[i][1] = lists.get(i)[1];
        }
        return res;
    }
}

这个比较好理解,如果看完上一道题目的题解,上一题我们是不断修改尾元素,这题我们不断更新newinterval, 目的是找到合适的插入位置,我们用index标记插入位置


再接再厉,一举突破这类题型:传送门

986

解法一:暴力,时间复杂度 : O ( N 2 ) O(N^2) O(N2),没有什么技巧可言

class Solution {
    public int[][] intervalIntersection(int[][] A, int[][] B) {
        List<int[]> lists = new ArrayList<>();
        int lenA = A.length;
        int lenB = B.length;
        for(int i = 0; i<lenA; i++){
            for(int j = 0; j<lenB; j++){
                if(A[i][0] > B[j][1] || B[j][0] > A[i][1]) continue;
                int[] temp = new int[2];
                temp[0] = Math.max(A[i][0], B[j][0]);
                temp[1] = Math.min(A[i][1], B[j][1]);
                lists.add(temp);
            }
        }
        int[][] res = new int[lists.size()][2];
        for(int i = 0; i<lists.size(); i++) {
            res[i][0] = lists.get(i)[0];
            res[i][1] = lists.get(i)[1];
        }
        return res;
    }
}

解法二:双指针法,时间复杂度为 O ( N ) O(N) O(N)

class Solution {
    public int[][] intervalIntersection(int[][] A, int[][] B) {
        List<int[]> lists = new ArrayList<>();
        int lenA = A.length;
        int lenB = B.length;
        int indexA = 0, indexB = 0;
        while(indexA < lenA && indexB < lenB){
            int start = Math.max(A[indexA][0],B[indexB][0]);
            int end = Math.min(A[indexA][1], B[indexB][1]);
            if(end >= start){ // 构成区间
                int[] temp = new int[2];
                temp[0] = start;
                temp[1] = end;
                lists.add(temp);
            }
            if(A[indexA][1] > B[indexB][1]) indexB++; // B的有边界小,靠前,B先走
            else indexA++;
        }
        int[][] res = new int[lists.size()][2];
        for(int i = 0; i<lists.size(); i++) {
            res[i][0] = lists.get(i)[0];
            res[i][1] = lists.get(i)[1];
        }
        return res;
    }
}

拿官方的例子来说 A = [[0,2],[5,10],[13,23],[24,25]] 和 B = [[1,5],[8,12],[15,24],[25,26]] A,B中谁的有边界靠前就让谁先走,构成了区间就加到结果集中


再来一道,加深印象:传送门

452

class Solution {
    public int findMinArrowShots(int[][] points) {
        int len = points.length;
        if(len <= 1) return len;
        Arrays.sort(points, (a, b)->{
            if(a[0] == b[0]) return Integer.compare(a[1],b[1]);
            return Integer.compare(a[0],b[0]);
        });
        int res = 1;
        int left = points[0][0];
        int right = points[0][1];
        for(int i = 1; i<len; i++){
            if(points[i][0] > right){
                left = points[i][0];
                right = points[i][1];
                res++;
            }else{
                left = Math.max(points[i][0], left);
                right = Math.min(points[i][1], right);
            }
        }
        return res;
    }
}

这题和上面那题区别不大,也是维持一个左右边界,这里有个坑一定要注意,数组元素相加减越界问题,一开始我用的是下面这种排序方式,结果越界了碰到这种情况 [[-2147483646,-2147483645],[2147483646,2147483647]]

Arrays.sort(points, (a,b)->a[0]==b[0]?a[1]-b[1]:a[0]-b[0]);

相信做完这几个例题,你碰到这类问题应该比较有把握解决。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值