13. Interval与扫描线

区间类的题目,很多可以用Greedy来做

Comparator的两种实现方法

  • 一种是先Implements一个类,然后Arrays.sort(intervals, new newComparator);

    public class newComparator implements Comparator<Object>{
         public int compare(Object a, Object b) {
             Interval aa = (Interval) a;
             Interval bb = (Interval) b;
             return aa.start - bb.start;
         }
    }
  • 另一种是写在Arrays.sort()里面,实践证明这种更快

    Arrays.sort(intervals, new Comparator<Interval>(){
              //compare by start time
              public int compare(Interval a, Interval b){
                  return a.start - b.start;
              }
    });

这题最简单的方法就是sort by start time,then check if(intervals[i].start < intervals[i-1].end) return false;

Meeting Rooms II

  • 给定一堆meetings, 要求返回需要最少room的数量

  • sort intervals by start time

  • minHeap compare with end time

  • for loop intervals, if cur.startTime < minHeap.peek().endTime, then push cur to heap, cnt++ ->NlogK

  • res = max(cnt)

class Solution {
    public int minMeetingRooms(int[][] intervals) {
        //sort intervals by start time ->NlogN
        //minHeap compare with end time
        //for loop intervals, if cur.startTime < minHeap.peek().endTime, then push cur to heap, cnt++ ->NlogK
        //res = max(cnt)
        //Time: NlogN
        //Space: O(K)
        if (intervals == null || intervals.length == 0) return 0;
        Arrays.sort(intervals, new Comparator<int[]>(){
            @Override
            public int compare(int[] a, int[] b) {
                if (a[0] == b[0]) {
                    return a[1] - b[1];
                }
                return a[0] - b[0];
            }
        });
        PriorityQueue<int[]> minHeap = new PriorityQueue<>(new Comparator<int[]>(){
            @Override
            public int compare(int[] a, int[] b) {
                return a[1] - b[1];
            }
        });
        int res = 0;
        for (int i = 0; i < intervals.length; i++) {
            if (minHeap.isEmpty() || minHeap.peek()[1] > intervals[i][0]) {
                minHeap.offer(intervals[i]);
            } else {
                minHeap.poll();
                minHeap.offer(intervals[i]);
            }
            res = Math.max(res, minHeap.size());
        }
        
        return res;
    }
}

还有一种扫描线解法

Merge Interval

  • sort intervals by start time

  • iterate each interval, compare cur.startTime with prev.EndTime

  • if cur.startTime < end, then end = Math.max(end, cur.endTime)

  • else push [begin, end] to res

class Solution {
    /*
    sort intervals by start time
    iterate each interval, compare cur.startTime with prev.EndTime
    if cur.startTime < end, then end = Math.max(end, cur.endTime)
    else push [begin, end] to res
    
    */
    public int[][] merge(int[][] intervals) {
        if (intervals == null || intervals.length == 0) {
            return new int[0][0];
        }
        List<int[]> list = new ArrayList<>();
        Arrays.sort(intervals, new Comparator<int[]>(){
            @Override
            public int compare(int[] a, int[] b) {
                return a[0] - b[0];
            }
        });
        int begin = intervals[0][0], end = intervals[0][1];
        for (int i = 0; i < intervals.length; i++) {
            if(intervals[i][0] <= end) {
                end = Math.max(end, intervals[i][1]);
            } else {
                int[] interval = new int[2];
                interval[0] = begin;
                interval[1] = end;
                list.add(interval);
                begin = intervals[i][0];
                end = intervals[i][1];
            }
        }
        int[] interval = new int[2];
        interval[0] = begin;
        interval[1] = end;
        list.add(interval);
        int[][] res = new int[list.size()][2];
        for (int i = 0; i < list.size(); i++) {
            res[i][0] = list.get(i)[0];
            res[i][1] = list.get(i)[1];
        }
        return res;
    }
    
    //05/05/21, while loop
    public int[][] merge(int[][] intervals) {
        //sort interval by start time in ascending order
        //[start,end], while (end >= curStart) -> end = curEnd and move to next
        Arrays.sort(intervals, new Comparator<int[]> () {
           @Override
            public int compare(int[] a, int[] b) {
                return a[0] - b[0];
            }
        });
        List<int[]> list = new ArrayList<>();
        
        int i = 0;
        while (i < intervals.length) {
            int start = intervals[i][0], end = intervals[i][1];
            int j = i+1;
            while (j < intervals.length && end >= intervals[j][0]) {
                end = Math.max(end, intervals[j][1]);
                j++;
            }
            int[] interval = new int[2];
            interval[0] = start;
            interval[1] = end;
            list.add(interval);
            i = j;
        }
        int[][] res = new int[list.size()][2];
        for (int k = 0; k < list.size(); k++) {
            res[k] = list.get(k);
        } 
        return res;
    }
}

  • 这题最简单的做法是把newInterval插到intervals里面,然后再调用上面的merge Interval的方法

  • 但是鉴于此题有个前提Given a set of non-overlapping intervals,所以用merge interval的话可能会浪费很多时间

  • 所以可以把intervals分成三部分,对于整个interval都在newinterval左边或者右边的我们可以直接add to res

  • 所以只需要处理有overlapping的情况,这里有个技巧就是用while loop实现之前的三部分分类,改变newInterval的start和end值,直到当前的interval完全在newInterval的右边,没有任何交集

  • 然后add newInterval, 再add the rest into res

  • 这题对于space可以有优化,in place solultion,但是因为list.remove(i)是O(N)操作,所以时间上会有牺牲

public class Solution {
    public List<Interval> insert(List<Interval> intervals, Interval newInterval) {
        List<Interval> res = new LinkedList<>();

        int i = 0;
        while(i < intervals.size() && intervals.get(i).end < newInterval.start){
            res.add(intervals.get(i));
            i++;
        }
        while(i < intervals.size() && intervals.get(i).start <= newInterval.end){
            newInterval.start = Math.min(intervals.get(i).start, newInterval.start);
            newInterval.end = Math.max(intervals.get(i).end, newInterval.end);
            i++;
        }
        res.add(newInterval);
        while(i < intervals.size()){
            res.add(intervals.get(i));
            i++;
        }
        return res;
    }
}

  • 这题从算法上没啥好说的

  • 就是注意corner case就好,比如[0,1] [7]

  • 还有解体要有模块化的思路

public class Solution {
    public List<String> summaryRanges(int[] nums) {
        List<String> res = new LinkedList<>();
        if(nums == null || nums.length < 1) return res;
        int start = nums[0];
        int end = nums[0];
        //int i = 1;

        for(int i = 1; i < nums.length; i++){
            if(nums[i - 1] + 1 != nums[i]){
                String range = getRange(start, end);
                res.add(range);
                start = nums[i];
            }
            end = nums[i];
        }
        String last = getRange(start, end);
        res.add(last);
        return res;
    }
    //[2,3,4]
    //[7]
    private String getRange(int start, int end){
        StringBuilder sb = new StringBuilder();
        if(start == end){
            sb.append(start);
        }else if(start < end){
            sb.append(start);
            sb.append("->");
            sb.append(end);
        }
        return sb.toString();
    }
}

  • 这题比summary ranges稍难,重在理清思路

  • 有个简单的方法是用lower做runner

  • 每次遍历num时,设upperBelow = num - 1,然后比较upperBelow和 lower的关系

  • 然后记得update lower = num + 1

  • 这样写的话题有个test case过不去[2147483647], lower = 0, upper = 2147483647。原因在于lower = num + 1之后就int overflow了,所以必须cast to long type

    public List<String> findMissingRanges(int[] nums, int lower, int upper) {
          List<String> res = new LinkedList<>();
          long low = (long) lower;
          long high = (long) upper;
          for(int num : nums){
             // if(lower > upper) break;
              long upperBelow = (long)num - 1;
              if(low == upperBelow){
                  res.add((int)low + "");
              }else if(low < upperBelow){
                  res.add((int)low + "->" + (int)upperBelow);
              }
              low = (long)num + 1;
          }
          if(low == high){
              res.add((int)low + "");
          }else if(low < high){
              res.add((int)low + "->" + (int) high);
          }
          return res;
      }

Skyline Problem

Last updated

Was this helpful?