Merge Intervals medium
Problem Statement
Given an array of intervals where intervals[i] = [starti, endi]
, merge all overlapping intervals, and return an array of the non-overlapping intervals that cover all the intervals in the input.
Example 1
Input: intervals = [[1,3],[2,6],[8,10],[15,18]] Output: [[1,6],[8,10],[15,18]] Explanation: Since intervals [1,3] and [2,6] overlaps, merge them into [1,6].
Example 2
Input: intervals = [[1,4],[4,5]] Output: [[1,5]] Explanation: Intervals [1,4] and [4,5] are considered overlapping.
Steps
-
Sort the intervals: Sort the intervals based on their start times. This ensures that we process overlapping intervals together. We use a custom comparator for this.
-
Initialize the merged intervals list: Create a list to store the merged intervals. The first interval from the sorted list will always be part of the merged intervals.
-
Iterate and merge: Iterate through the sorted intervals. For each interval:
- If the current interval overlaps with the last interval in the merged list (its start time is less than or equal to the end time of the last merged interval), merge them by updating the end time of the last merged interval to the maximum of the current interval's end time and the last merged interval's end time.
- If there's no overlap, add the current interval to the merged intervals list.
-
Return the merged intervals: After processing all intervals, the merged intervals list contains the non-overlapping intervals covering all input intervals.
Explanation
The algorithm's core idea is based on sorting and greedy approach. Sorting allows us to process intervals in a sequential manner, making it easier to identify and handle overlaps. The greedy approach, in choosing the maximum end time during merging, ensures we find the minimal set of non-overlapping intervals that cover all input intervals.
Code
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
class Solution {
public int[][] merge(int[][] intervals) {
if (intervals == null || intervals.length == 0) {
return new int[0][0];
}
// Sort intervals by start time
Arrays.sort(intervals, Comparator.comparingInt(a -> a[0]));
List<int[]> mergedIntervals = new ArrayList<>();
mergedIntervals.add(intervals[0]); // Add the first interval
for (int i = 1; i < intervals.length; i++) {
int[] currentInterval = intervals[i];
int[] lastMergedInterval = mergedIntervals.get(mergedIntervals.size() - 1);
// Check for overlap
if (currentInterval[0] <= lastMergedInterval[1]) {
// Merge intervals
lastMergedInterval[1] = Math.max(lastMergedInterval[1], currentInterval[1]);
} else {
// No overlap, add current interval
mergedIntervals.add(currentInterval);
}
}
// Convert List<int[]> to int[][]
return mergedIntervals.toArray(new int[0][]);
}
}
Complexity
- Time Complexity: O(N log N), dominated by the sorting step. N is the number of intervals.
- Space Complexity: O(N) in the worst case (when no intervals overlap), to store the merged intervals. In the best case (all intervals overlap), it's O(1). The space used for sorting might vary depending on the sorting algorithm used by
Arrays.sort()
. In many Java implementations, it's O(log N) extra space for recursion.