You are given an array of intervals, where each interval is represented as a pair of integers [start, end]
. Your task is to merge all overlapping intervals and return a new array of non-overlapping intervals that cover all the intervals in the input.
For example, if you have the intervals [[1,3],[2,6],[8,10],[15,18]]
, the output should be [[1,6],[8,10],[15,18]]
. The intervals [1,3]
and [2,6]
overlap, so they are merged into [1,6]
.
Here are a few more examples to illustrate the problem:
[[1,4],[4,5]]
[[1,5]]
(The intervals [1,4] and [4,5] are considered overlapping.)[[1,3],[2,6],[8,10],[15,18]]
[[1,6],[8,10],[15,18]]
[[1,4],[0,4]]
[[0,4]]
[[1,4],[0,0]]
[[0,0],[1,4]]
[[1,4],[0,2],[3,5]]
[[0,5]]
Write a function mergeIntervals(intervals)
that takes an array of intervals as input and returns a new array of merged, non-overlapping intervals. The input array intervals
is a list of lists where each inner list has exactly two elements: the start and end of the interval. The function should return a list of lists with the same structure, representing the merged intervals. The order of the output intervals does not matter.
Let's tackle the "Merge Intervals" problem. I've solved this a few times, so I have a good understanding of the nuances. I've been a Software Engineer at Google for 8 years now, working primarily on distributed systems, and have seen variations of this problem crop up in scheduling and resource allocation.
Given a collection of intervals, merge all overlapping intervals.
The most straightforward approach is to iterate through all pairs of intervals and check if they overlap. If they do, merge them and repeat the process until no more merges are possible.
Algorithm:
i
in the list:j
in the list (where j != i
):i
and j
overlap (i.e., i.start <= j.end
and i.end >= j.start
).i
and j
into a new interval and remove i
and j
from the list. Add the new interval to the list.Code (Python):
python def merge_intervals_naive(intervals): merged = False n = len(intervals) i = 0 while i < len(intervals): j = i + 1 while j < len(intervals): if intervals[i][0] <= intervals[j][1] and intervals[i][1] >= intervals[j][0]: # Overlap found, merge the intervals new_interval = [min(intervals[i][0], intervals[j][0]), max(intervals[i][1], intervals[j][1])] intervals.pop(j) # Remove j first as it is the higher index intervals.pop(i) intervals.insert(i, new_interval) merged = True break # Restart inner loop because the list has changed else: j += 1 if not merged: i += 1 # Only increment if no merge happened, otherwise stay in the same place and merge merged = False return intervals
intervals = [[1,3],[2,6],[8,10],[15,18]] print(merge_intervals_naive(intervals))
Big O Notation:
new_interval
could technically be O(1) space in each merge operation, so, considering memory allocation in general, O(m), where m is the max number of merge operations done in a single pass. More practically, it tends to be O(1).The optimal solution involves sorting the intervals by their start times and then iterating through the sorted list, merging overlapping intervals as we go.
Algorithm:
merged_intervals
.merged_intervals
.merged_intervals
, merge them.merged_intervals
.merged_intervals
.Code (Python):
python def merge_intervals_optimal(intervals): # Sort the intervals by start time intervals.sort(key=lambda x: x[0])
merged_intervals = []
if not intervals:
return merged_intervals
merged_intervals.append(intervals[0])
for i in range(1, len(intervals)):
current_interval = intervals[i]
last_merged_interval = merged_intervals[-1]
# Check for overlap
if current_interval[0] <= last_merged_interval[1]:
# Merge the intervals
merged_intervals[-1] = [last_merged_interval[0], max(last_merged_interval[1], current_interval[1])]
else:
# No overlap, add the current interval to merged_intervals
merged_intervals.append(current_interval)
return merged_intervals
intervals = [[1,3],[2,6],[8,10],[15,18]] print(merge_intervals_optimal(intervals))
Big O Notation:
merged_intervals
will contain all the original intervals. In place sorting algorithms may reduce this to O(log n) or even O(1) in some implementations.[[1, 4], [2, 3]]
).[[1, 3], [1, 3]]
).sort()
ensures this.The optimal solution provides a significant improvement in time complexity compared to the brute-force approach. The sorting step allows us to efficiently identify and merge overlapping intervals in a single pass.