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] overlap, 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.
Constraints:
1 <= intervals.length <= 104
intervals[i].length == 2
0 <= starti <= endi <= 104
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force approach to merging intervals means comparing each interval with every other interval to see if they overlap. If any intervals overlap, we merge them, and then repeat the whole process from the start. This ensures that all possible merges are considered, eventually leaving us with a list of non-overlapping, merged intervals.
Here's how the algorithm would work step-by-step:
def merge_intervals_brute_force(intervals):
merged = True
while merged:
merged = False
interval_index = 0
while interval_index < len(intervals):
other_interval_index = interval_index + 1
while other_interval_index < len(intervals):
# Check for overlap, merge if found
if (intervals[interval_index][1] >= intervals[other_interval_index][0] and
intervals[other_interval_index][1] >= intervals[interval_index][0]):
# Need to merge since intervals overlap
intervals[interval_index] = [
min(intervals[interval_index][0],
intervals[other_interval_index][0]),
max(intervals[interval_index][1],
intervals[other_interval_index][1])
]
del intervals[other_interval_index]
merged = True
# Break to restart from the beginning of inner loop
break
other_interval_index += 1
# Break out of outer loop to restart entire process if merged
if merged:
break
interval_index += 1
return intervals
The trick to solving this problem efficiently is to first arrange the intervals based on their start times. Then, we can go through them in order and merge them when they overlap, creating a new consolidated interval.
Here's how the algorithm would work step-by-step:
def merge_intervals(intervals):
# Sort intervals based on start times.
intervals.sort(key=lambda interval: interval[0])
merged_intervals = []
# Add the first interval to the merged list.
if intervals:
merged_intervals.append(intervals[0])
for interval in intervals[1:]:
# Check for overlap with the last merged interval.
if merged_intervals[-1][1] >= interval[0]:
# Merge the intervals.
merged_intervals[-1] = [merged_intervals[-1][0], max(merged_intervals[-1][1], interval[1])]
else:
# No overlap, add the interval to the merged list.
merged_intervals.append(interval)
return merged_intervals
Case | How to Handle |
---|---|
Empty input intervals array | Return an empty array since there are no intervals to merge. |
Null input intervals array | Throw an IllegalArgumentException or return null after checking for null input. |
Single interval in the input array | Return the input array as is, since there's nothing to merge. |
Intervals with overlapping start and end values (e.g., [1, 1]) | The algorithm should correctly merge or include these intervals without errors. |
Intervals with negative start and end values | The algorithm should handle negative values correctly, assuming the comparison logic accounts for them. |
Intervals already sorted in ascending order | The algorithm should still function correctly, potentially with improved performance if sorting is skipped or optimized. |
Intervals sorted in descending order | The algorithm needs to correctly sort the intervals in ascending order to ensure proper merging. |
Large range of interval values causing potential integer overflow | Use appropriate data types (e.g., long) to store interval boundaries to prevent potential integer overflows during calculations. |