High frequency questions - Special Topics on Interval related issues

Posted by cuboidgraphix on Fri, 21 Jan 2022 10:29:50 +0100

The blog refers to the following Blogs:

https://emre.me/coding-patterns/merge-intervals/

https://leetcode.com/discuss/general-discussion/794725/General-Pattern-for-greedy-approach-for-Interval-based-problems/658450

Interval means interval in Chinese, which can be understood as interval. Many in life can be described as intervals, such as meeting time interval, bus operation station interval, etc. Generally speaking, the time interval is good. Most topics can be understood according to the time interval, which helps us understand the specific scene of the topic.

Given two intervals A and B, there are six different ways to the relationship between the two intervals:

However, if a.start < = b.start, that is, the given interval is orderly arranged with the start time, there are only 1, 2 and 3 cases, of which 2 and 3 are overlap. If we want to merge the intersecting a and B intervals, the corresponding result c is shown in the figure:

The formula given is:

c.start = a.start 

//The start of c takes the start of a, that is, min(a.start, b.start). Because a and B are orderly, it is a.start

c.end = max(a.end, b.end)

//The end time of c takes the latest value of the end time of a and b

                    

If we want to judge whether the two interval s intersect, as shown in the above figure, we only need to judge whether the start time of B is greater than the end time of a (b.start > a.end). Obviously, if it is greater than, it will not intersect, but less than the inevitable intersection. Equal to whether it means intersection depends on the provisions of the topic.

       https://leetcode.com/discuss/general-discussion/794725/General-Pattern-for-greedy-approach-for-Interval-based-problems/658450

A template for solving interval related problems is given:

public boolean genericIntervalTemplate(int[][] intervals) {
    if (intervals.length == 0) //For legitimacy check, you generally need to check whether the intervals is empty (* return the corresponding result according to the actual situation)
        return $result;
    
    Arrays.sort(intervals, (a, b) -> a[0] - b[0]); // Sorting according to the start time is the key to solving the interval problem. The time complexity is O(nlogn)
    
    int[] prev = intervals[0]; // Save a prev pointer to save the last result. By default, it is assigned as the first interval
    for (int i = 1; i < intervals.length; i++) { // Starting from 1, check the remaining intervals in the intervals set
        int[] current = intervals[i];   // Current interval curr to process
        if (doIntervalsOverlap(prev, current)) {  // If there is an intersection, handle it (* return the corresponding result according to the actual situation)
            // if overlap Do business logic
        }
        prev = current; // Update the result saved by the prev pointer to the current interval (* return the corresponding result according to the actual situation)
    }
    return $result; // Return result (* return the corresponding result according to the actual situation)
}

private boolean doIntervalsOverlap(int[] i1, int[] i2) {
    //if the start of the second interval is greater or equal than the end of the first interval they dont overlap
    // > or >= depends on the exact problem
    // for example in some cases [4,6] [6,10] are considered overlapping, in some not
    if (i2[0] >= i1[1])
        return false;
    return true;
}

Pay attention to three points:

  1. Intervals must be sorted;
  2. The edge of the Interval, such as whether [4,6] [6,10] is an overlap, needs to be specified according to the topic;
  3. If there is sorting, the time complexity is O(nlogn), and only O(n) has been processed for ordered intervals.

Let's look directly at the specific topics:

https://leetcode.com/problems/merge-intervals/

This problem is that merge has an overlap interval, which is directly applied to the template (the problem is completed before, which is a little different from the template, but the idea is completely the same):

public int[][] merge(int[][] intervals) {
        
        // Return value
        LinkedList<int[]> res = new LinkedList<>();
        
        // The title doesn't say that the array is orderly, so it needs to be sorted
        Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
        
        // c after merge: 
        // c.start = a.start 
        // c.end = max(a.end, b.end)
        // First, add the first subarray to res and keep the subarray as a reference for subsequent dynamic changes
        // The end of the previous result is less than the start of the current subarray. There is no crossover, and it is directly add ed to the result
        // Otherwise (the end of the previous result is greater than or equal to the start of the current sub array, and there is a crossover. At this time, only the end value needs to be modified)
        int[] newInterval = intervals[0];
        res.add(newInterval);
        for (int[] curr : intervals) {
            // There is crossover
            if (curr[0] <= newInterval[1]) {
                newInterval[1] = Math.max(curr[1], newInterval[1]);
            } else {
                newInterval = curr;
                res.add(newInterval);
            }
        }
        
        // The first parameter can pass res.size, but it is also possible to pass 0. It will be replaced with res.size in the implementation
        return res.toArray(new int[0][]);
}

	

https://leetcode.com/problems/insert-interval/

This question is different from the previous one, because it needs to insert interval, so let's change our thinking.

Understand the given intervals as a scheduled meeting, so define a global pointer i, and see how to insert a new meeting from left to right.

If the new meeting starts later than the scheduled meeting, these meetings can be retained in the results;

overlap exists until the start time of the new meeting is found earlier than the end time of the scheduled meeting. Note that the cycle condition at this time is that as long as the start time of the existing meeting is less than or equal to the end time of the new meeting, it will enter the cycle and merge the meeting time.

Finally, the remaining irrelevant meetings of the out cycle are scheduled to be added to the result, as shown in the following diagram:

If it is difficult to understand, refer to the video: https://www.youtube.com/watch?v=E9IYRG_WYcM

The code is as follows:

public int[][] insert(int[][] intervals, int[] newInterval) {
        List<int[]> res = new ArrayList<>();

        int i = 0; // Global pointer

        // Add the interval not related to newInterval on the left to the result
        while(i < intervals.length && newInterval[0] > intervals[i][1]) {
            res.add(intervals[i]);
            i++;
        }

        // If the end of newInterval is greater than or equal to the beginning of interval, a merge relationship will occur
        // The merge process takes the minimum value on the left for the left boundary and the maximum value on the right boundary
        while(i < intervals.length && newInterval[1] >= intervals[i][0]) {
            newInterval[0] = Math.min(newInterval[0], intervals[i][0]);
            newInterval[1] = Math.max(newInterval[1], intervals[i][1]);
            i++;
        }

        res.add(newInterval);

        // Add the irrelevant right intervals section to the result
        while(i < intervals.length) {
            res.add(new int[]{intervals[i][0], intervals[i][1]});
            i++;
        }

        return res.toArray(new int[0][]);
    }

The following are two questions about meeting room. Because I don't have a member of leetcode, this question is screenshot from lintcode. The questions are the same:

https://www.lintcode.com/problem/920/description

This question is very simple, and you only need to return true or false, and directly apply the template:

public boolean canAttendMeetings(List<Interval> intervals) {
       
       if (intervals.isEmpty()) return true;
        Collections.sort(intervals, (a, b) -> a.start - b.start);

       Interval prev = intervals.get(0);

       for (int i = 1; i < intervals.size(); i++) {
           if (intervals.get(i).start < prev.end) {
               return false;
           } 
           prev = intervals.get(i);
       }
        return true;
}

https://www.lintcode.com/problem/919/description

This question requires the minimum number of meeting rooms, which is slightly different from the above question. The main difference is how to place the time period of the current meeting. You need to find the earliest end time of the previously placed time period. If the start time of the current time period is later than this time, it can be placed behind the meeting time period, that is, there is no need for a new meeting room. Therefore, you need to store all previous meeting slots. Considering that the meeting that needs to be out of the stack each time is the earliest meeting that ended before, the small root heap is used for storage, and the minimum end time is used as the standard for each out of the stack.

The placement diagram is as follows:

The code is as follows:

public int minMeetingRooms(List<Interval> intervals) {
        if (intervals.isEmpty() || intervals.size() == 1) return intervals.size();
        Collections.sort(intervals, (a, b) -> a.start - b.start);

        // The small root heap is used to store the meeting, and the earliest time to end the meeting is used to get out of the heap
        PriorityQueue<Interval> minHeap = new PriorityQueue<>((a, b) -> a.end - b.end);
        minHeap.offer(intervals.get(0));

        for (int i = 1; i < intervals.size(); i++) {
            Interval prev = minHeap.poll();
            Interval curr = intervals.get(i);
            if (curr.start >= prev.end) {
                // If there is no crossover, it can be allocated to the same time to extend the end time
                prev.end = Math.max(prev.end, curr.end);
            } else { // Cross, offer current time interval
                minHeap.offer(curr);
            }
            minHeap.offer(prev);
        }
        return minHeap.size();
    }

Topics: leetcode