Leetcode 1288. Remove Covered Intervals

Problem Explanation:

The problem requires us to check for intervals that are overshadowed by other intervals in a list and get rid of such intervals. One interval [a,b) is said to be covered by another interval [c,d) if c is less than or equal to a and b is less than or equal to d.

After we remove all covered intervals, we simply return the count of remaining intervals.

We will understand this better with an example:

Example:

Consider the following set of intervals: [[1,4],[3,6],[2,8]]

If we examine closely, we see that the interval [3,6] is completely covered by [2,8] since 2 <= 3 and 6 <= 8.

So we can get rid of interval [3,6]. Now we are left out with [[1,4], [2,8]]. The count of intervals left out after removing covered intervals would therefore be 2.

Approach Explanation:

The solution sorts the list of intervals in ascending order based on the start value of the intervals. If the start value for two intervals is the same, the interval with larger end value comes first in the sorted list.

Then we iterate over the sorted intervals and maintain a record of the maximum end value (prevEnd) encountered so far. If the end value of the current interval is greater than prevEnd, then it's a valid uncovered interval, as it extends out past all previous intervals. We increment the count of valid intervals and update the prevEnd.

Let's go through the solution step by-step using the same example: [[1,4],[3,6],[2,8]]

  1. After sorting the intervals, we get [[1,4],[2,8],[3,6]]
  2. Initialize prevEnd as 0 and the count of valid intervals as 0.
  3. The first interval is [1,4]. The end value 4 is greater than prevEnd (which is 0), so it's a valid uncovered interval. We increment the count of valid intervals and update prevEnd to 4.
  4. The next interval is [2,8]. The end value 8 is greater than prevEnd (which is 4), so it's also a valid uncovered interval. We increment the count of valid intervals and update prevEnd to 8.
  5. The next interval is [3,6]. The end value 6 is not greater than prevEnd (which is 8), so it's a covered interval. We do nothing and move on.
  6. Now, we have examined each interval and the count of valid uncovered intervals is 2.

Code Solutions:

Python Solution:

Here is a python solution for the problem:

1
2python
3class Solution:
4    def removeCoveredIntervals(self, intervals):
5        # Sort intervals
6        intervals.sort(key=lambda x: (x[0], -x[1]))
7        
8        count = 0
9        prevEnd = 0
10        
11        # Loop through intervals
12        for _, b in intervals:
13            # Check if interval is covered
14            if b > prevEnd:
15                count += 1
16                prevEnd = b
17                
18        return count

Java Solution:

The same logic can be implemented in Java as follows:

1
2java
3class Solution {
4    public int removeCoveredIntervals(int[][] intervals) {
5        Arrays.sort(intervals, (a,b) -> a[0] == b[0] ? b[1] - a[1] : a[0] - b[0]);
6        
7        int count = 0, end = 0;
8        
9        for(int[] curr : intervals) {
10            if(curr[1]>end) {
11                count++;
12                end = curr[1];
13            }
14        }
15        
16        return count;
17    }
18}

JavaScript Solution:

Here is the JavaScript version of above solution:

1
2javascript
3class Solution {
4 removeCoveredIntervals(intervals) {
5    intervals.sort((a,b) => a[0] === b[0] ? b[1] - a[1] : a[0] - b[0]);
6    
7    let count = 0, end = 0;
8    
9    for(let i=0; i<intervals.length; i++) {
10        if(intervals[i][1] > end) {
11            count++;
12            end = intervals[i][1];
13        }
14    }
15    
16    return count;
17   }
18}

C++ Solution:

In C++, the solution could be implemented as follows:

1
2cpp
3class Solution {
4public:
5    int removeCoveredIntervals(vector<vector<int>>& intervals) {
6        sort(intervals.begin(), intervals.end(), [](const vector<int>& a, const vector<int>& b){ 
7            if(a[0] == b[0])
8                return a[1] > b[1];
9            return a[0] < b[0];
10        });
11        
12        int count = 0, end = 0;
13        
14        for(auto interval : intervals) {
15            if(interval[1] > end) {
16                count++;
17                end = interval[1];
18            }
19        }
20        
21        return count;
22    }
23};

C# Solution:

In C#, you can solve the problem like this:

1
2csharp
3class Solution {
4    public int RemoveCoveredIntervals(int[][] intervals) {
5        Array.Sort(intervals, (a,b)=> a[0]==b[0] ? b[1]-a[1] : a[0]-b[0] );
6        
7        int count=0, end=0;
8        
9        foreach( var curr in intervals) {
10            if(curr[1]>end) {
11                count++;
12                end = curr[1];
13            }
14        }
15        
16        return count;
17    }
18}

In all the above solutions, we sort the intervals, and then iterate over them, updating the count and end value when we find valid uncovered intervals. The sort() and Array.Sort() functions may vary in syntax across languages, but they are used for the same purpose.This strategy is highly efficient and works in O(n log n) time complexity, where n is the number of intervals. Note that the sorting operation dominates the time complexity. After sorting, we only loop through the intervals once, which gives a linear, O(n), time complexity.

If the problem requires an even more efficient solution, we could opt for using more sophisticated data structures like interval trees. However, this may increase space complexity and code complexity for a marginal improvement in performance, so in this specific case, the sorting solution provides a good balance between complexity and performance.

To summarise, for solving such interval coverage problems, the general strategy is to:

  1. Sort the intervals based on their start and end points.
  2. Iterate over the sorted intervals and keep track of the uncovered intervals.
  3. Update the count and the maximum end point encountered so far on finding an uncovered interval.

This strategy works because sorting the intervals allows us to make certain assumptions while iterating through them. Any interval that does not extend the maximum end encountered so far can be safely deemed as covered by a previous interval and ignored, helping us keep track of only the relevant uncovered intervals efficiently.

We hope the above set of solutions in multiple languages helps you better understand how to tackle problems related to intervals and overlapping segments in data.


Got a question? Ask the Teaching Assistant anything you don't understand.

Still not clear? Ask in the Forum,  Discord or Submit the part you don't understand to our editors.


TA 👨‍🏫