Interval 类型总结

本文汇总了面试中常见的Interval类型问题,包括Merge Intervals、Insert Interval、Interval List Intersections、Remove Interval、Remove Covered Intervals和Non-overlapping Intervals等。通过分析解题策略,如按start排序、判断区间相交、分情况讨论等,帮助读者理解和解决这类问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Interval是一类型的题目,面试的很喜欢出,这里把所有interval的题目全部总结一下;

Merge Intervals 首先按照start sort之后,判断end是否跟start相交,如果相交,end就是两者最大值;否则加入cur;注意最后需要加入cur;

class Solution {    
    public int[][] merge(int[][] intervals) {
        if(intervals == null || intervals.length == 0) {
            return new int[0][0];
        }
        // sort based on start;
        Arrays.sort(intervals, (a, b) -> (a[0] - b[0]));
        List<int[]> list = new ArrayList<int[]>();
        int[] last = new int[]{intervals[0][0], intervals[0][1]};
        for(int i = 1; i < intervals.length; i++) {
            int[] cur = new int[]{intervals[i][0], intervals[i][1]};
            if(last[1] < cur[0]) {
                list.add(last);
                last = cur;
            } else {
                last[1] = Math.max(last[1], cur[1]);
            }
        } 
        list.add(last); // 注意最后需要加入last;
        
        int[][] result = new int[list.size()][2];
        int index = 0;
        for(int[] interval: list) {
            result[index][0] = interval[0];
            result[index][1] = interval[1];
            index++;
        }
        return result;
    }
}

Insert Interval 思路:分三种情况,一种是interval 在newInterval前面, 一种是newInterval在interval前面,一种是相交;

class Solution {
    public int[][] insert(int[][] intervals, int[] newInterval) {
        if(intervals == null || intervals.length == 0) {
            int[][] res = new int[1][2];
            res[0][0] = newInterval[0];
            res[0][1] = newInterval[1];
            return res;
        }
        
        List<int[]> list = new ArrayList<int[]>();
        for(int[] interval: intervals) {
            if(interval[1] < newInterval[0]) {
                // interval, newInterval;
                list.add(interval);
            } else if(newInterval[1] < interval[0]) {
                // newInterval, interval;
                list.add(newInterval);
                // 不要忘记newInterval往后挪动;
                newInterval = interval;
            } else {
                newInterval[0] = Math.min(newInterval[0], interval[0]);
                newInterval[1] = Math.max(newInterval[1], interval[1]);
            }
        }
        // 不要忘记加newInterval;
        list.add(newInterval);
        
        int[][] res = new int[list.size()][2];
        for(int i = 0; i < list.size(); i++) {
            int[] temp = list.get(i);
            res[i][0] = temp[0];
            res[i][1] = temp[1];
        }
        return res;
    }
}

Interval List Intersections 跟Time Intersection一样,因为是有序的,可以直接打擂台的方式,扫描,start取最大值,end取最小值,如果start <= end就加入 res。否则,淘汰掉end在前面的那个interval;

class Solution {
    public int[][] intervalIntersection(int[][] A, int[][] B) {
        List<int[]> list = new ArrayList<int[]>();
        int a = 0; int b = 0;
        while(a < A.length && b < B.length) {
            // { A[a][0], A[a][1] };
            // { B[b][0], B[b][1] };
            int start = Math.max(A[a][0], B[b][0]);
            int end = Math.min(A[a][1], B[b][1]);
            if(start <= end) {
                list.add(new int[] {start, end});
            } 
            
            // 无论相交不相交,都要从a, b中间踢走一个;
            // start >= end;
            if(A[a][1] > B[b][1]) {
                b++;
            } else {
                a++;
            }
        }
        int[][] res = new int[list.size()][2];
        int index = 0;
        for(int[] item: list) {
            res[index][0] = item[0];
            res[index][1] = item[1];
            index++;
        }
        return res;
    }
}

Remove Interval 就是分情况讨论,cur在delete的哪个部位,相应做处理;非常适合电面,这题;

class Solution {
    public List<List<Integer>> removeInterval(int[][] intervals, int[] toBeRemoved) {
        List<List<Integer>> lists = new ArrayList<List<Integer>>();
        if(intervals == null || intervals.length == 0) {
            return lists;
        }
        
        int removeStart = toBeRemoved[0];
        int removeEnd = toBeRemoved[1];
        for(int[] interval : intervals) {
            int start = interval[0];
            int end = interval[1];
            // no overlap;
            if(end <= removeStart || removeEnd <= start) {
                lists.add(Arrays.asList(start, end));
            }   
            // 前面overlap
            if(start < removeStart && removeStart < end) {
                lists.add(Arrays.asList(start, removeStart));
            }
            // 完全在delete中间;
            if(removeStart < start && end < removeEnd) {
                continue;
            }
            // 后面overlap;
            if(start < removeEnd && removeEnd < end) {
                lists.add(Arrays.asList(removeEnd, end));
            }
        }
        return lists;
    }
}

Remove Covered Intervals 核心思想就是:算overlap了多少个,然后用总个数减去overlap的个数。如何计算overlap就是:sort array , 首先按照x sort,如果x相同,y按照从大到小sort,这样只有前面的能够cover后面的,后面的不能cover前面的,因为x都排序了,y是按照从大到小排序的。

class Solution {
    public int removeCoveredIntervals(int[][] intervals) {
        if(intervals == null || intervals.length == 0) {
            return 0;
        }
        int[] last = null;
        Arrays.sort(intervals, (a, b) -> (a[0] != b[0] ? a[0] - b[0] : b[1] - a[1]));
        int count = 0;
        for(int[] interval : intervals) {
            if(last == null) {
                last = interval;
            } else {
                if(last[1] >= interval[1]) {
                    count++;
                } else {
                    last = interval;
                }
            }
        }
        return intervals.length - count;
    }
}

Non-overlapping Intervals 按照start,排序,判断是否有重复也就是end是否大于后面的start,如果大于,就有重复,重复的话,要去掉的就是end最大的那个,也就是保留下end比较小的,这样去掉end最大的,也就是保证了remove最少的;

class Solution {
    public int eraseOverlapIntervals(int[][] intervals) {
        if(intervals == null || intervals.length == 0) {
            return 0;
        }
        Arrays.sort(intervals, (a, b) -> (a[0] != b[0] ? a[0] - b[0] : b[1] - a[1]));
        int[] cur = null;
        int count = 0;
        for(int[] interval : intervals) {
            if(cur == null) {
                cur = interval;
            } else {
                if(cur[1] > interval[0]) {
                    count++;
                    cur[1] = Math.min(cur[1], interval[1]);
                } else {
                    // cur[1] <= interval[0];
                    cur = interval;
                }
            }
        }
        return count;
    }
}

Find Right Interval 思路:题目要求很明显就是找第一个比自己大的start >= 我当前的end。所以找第一个比自己大,这个东西很好用treemap, 每次查找是logn, ceilingKey来找第一个比自己大的interval start,treemap里面存start和index;

class Solution {
    public int[] findRightInterval(int[][] intervals) {
        if(intervals == null || intervals.length == 0) {
            return new int[0];
        }
        int n = intervals.length;
        TreeMap<Integer, Integer> treemap = new TreeMap<>();
        for(int i = 0; i < n; i++) {
            treemap.put(intervals[i][0], i);
        }
        
        int[] res = new int[n];
        for(int i = 0; i < n; i++) {
            Integer ceilingKey = treemap.ceilingKey(intervals[i][1]);
            if(ceilingKey == null) {
                res[i] = -1;
            } else {
                res[i] = treemap.get(ceilingKey);
            }
        }
        return res;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值