56. Merge Intervals - Explanation
Description
Given an array of intervals where intervals[i] = [start_i, end_i], merge all overlapping intervals, and return an array of the non-overlapping intervals that cover all the intervals in the input.
You may return the answer in any order.
Note: Intervals are non-overlapping if they have no common point. For example, [1, 2] and [3, 4] are non-overlapping, but [1, 2] and [2, 3] are overlapping.
Example 1:
Input: intervals = [[1,3],[1,5],[6,7]]
Output: [[1,5],[6,7]]Example 2:
Input: intervals = [[1,2],[2,3]]
Output: [[1,3]]Constraints:
1 <= intervals.length <= 1000intervals[i].length == 20 <= start <= end <= 1000
Topics
Recommended Time & Space Complexity
You should aim for a solution as good or better than O(nlogn) time and O(n) space, where n is the size of the input array.
Hint 1
Sorting the given intervals in ascending order based on their start values is beneficial, as it helps in identifying overlapping intervals efficiently. How can you determine if two intervals overlap?
Hint 2
If two intervals are sorted in ascending order by their start values, they overlap if the start value of the second interval is less than or equal to the end value of the first interval.
Hint 3
We iterate through the sorted intervals from left to right, starting with the first interval in the output list. From the second interval onward, we compare each interval with the last appended interval. Can you determine the possible cases for this comparison?
Hint 4
The two cases are: if the current interval overlaps with the last appended interval, we update its end value to the maximum of both intervals' end values and continue. Otherwise, we append the current interval and proceed.
Prerequisites
Before attempting this problem, you should be comfortable with:
- Sorting - Required to process intervals in order by start time for efficient merging
- Intervals - Understanding how to represent, compare, and merge overlapping ranges
- Sweep Line Algorithm - Alternative approach that tracks active intervals using events on a timeline
1. Sorting
Intuition
We are given a list of intervals, and some of them may overlap.
The goal is to merge all overlapping intervals so that the final list contains only non-overlapping intervals, covering the same ranges.
A natural way to approach this is:
- if intervals are processed in sorted order by start time, then
- any overlap can only happen with the most recently added interval
So after sorting:
- we keep track of the last merged interval
- if the current interval overlaps with it, we merge them
- otherwise, we start a new interval
Algorithm
- Sort all intervals by their start time.
- Initialize the result list
outputwith the first interval. - Iterate through each interval
(start, end)in the sorted list: - Let
lastEndbe the end of the last interval inoutput. - If the current interval overlaps with the last one (
start <= lastEnd):- Merge them by updating the end:
output[-1][1] = max(lastEnd, end)
- Merge them by updating the end:
- Otherwise (no overlap):
- Append the current interval to
outputas a new interval
- Append the current interval to
- After processing all intervals, return
output
class Solution:
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
intervals.sort(key=lambda pair: pair[0])
output = [intervals[0]]
for start, end in intervals:
lastEnd = output[-1][1]
if start <= lastEnd:
output[-1][1] = max(lastEnd, end)
else:
output.append([start, end])
return outputTime & Space Complexity
- Time complexity:
- Space complexity:
- or space depending on the sorting algorithm.
- for the output list.
2. Sweep Line Algorithm
Intuition
This approach treats each interval as a pair of events on a number line:
- an interval starts at
start - an interval ends at
end
Instead of merging intervals directly, we track how many intervals are currently active as we move from left to right along the number line.
The key idea:
- when the number of active intervals goes from
0 → positive, a merged interval starts - when it goes from
positive → 0, a merged interval ends
By recording how the count changes at each boundary and sweeping through them in order, we can reconstruct all merged intervals.
Algorithm
- Create a map
mpto store boundary changes:- for each interval
[start, end]:- increment
mp[start](interval starts) - decrement
mp[end](interval ends)
- increment
- for each interval
- Initialize:
have = 0to track how many intervals are currently activeinterval = []to build the current merged intervalres = []to store the final merged intervals
- Iterate through all keys in
mpin sorted order: - For each position
i:- If
intervalis empty, this position may mark the start of a new merged interval:- set
interval = [i]
- set
- Update the active count:
have += mp[i]
- If
have == 0:- all intervals have closed at this point
- append
ias the end of the current merged interval - add
intervaltores - reset
intervalto empty
- If
- After processing all events, return
res
class Solution:
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
mp = defaultdict(int)
for start, end in intervals:
mp[start] += 1
mp[end] -= 1
res = []
interval = []
have = 0
for i in sorted(mp):
if not interval:
interval.append(i)
have += mp[i]
if have == 0:
interval.append(i)
res.append(interval)
interval = []
return resTime & Space Complexity
- Time complexity:
- Space complexity:
3. Greedy
Intuition
We want to merge all overlapping intervals so the result contains only non-overlapping ranges.
This solution uses a greedy idea with an auxiliary array:
- For every possible start point
start, we record the farthest end of any interval that begins atstart - Then we scan from left to right, maintaining the farthest point we must still cover (
have)
While scanning:
- if we see an interval starting at position
i, we may need to extend our current merged interval’s end to include it - once our scan index reaches the farthest required end, we can safely close the current merged interval
So the scan behaves like:
- “start a merged interval when we first see coverage”
- “keep extending its end while overlaps exist”
- “close it when we finish the coverage”
Algorithm
- Find the maximum start value among all intervals (
max_val). - Create an array
mpof sizemax_val + 1:mp[start]will store the farthest(end + 1)among intervals that start atstart
- For each interval
[start, end]:- update
mp[start] = max(mp[start], end + 1)
- update
- Initialize:
resas an empty list for merged intervalsinterval_start = -1to mark the start of the current merged intervalhave = -1to track the farthest end of the current merged interval
- Scan
ifrom0tolen(mp) - 1:- If
mp[i] != 0, it means some interval starts ati:- if we are not already in a merged interval, set
interval_start = i - update
have = max(have, mp[i] - 1)(convert back to real end)
- if we are not already in a merged interval, set
- If
have == i, it means we reached the end of the current merged interval:- append
[interval_start, have]tores - reset
interval_startandhave
- append
- If
- After the scan, if a merged interval is still open, append it to
res. - Return
res.
class Solution:
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
max_val = max(interval[0] for interval in intervals)
mp = [0] * (max_val + 1)
for start, end in intervals:
mp[start] = max(end + 1, mp[start])
res = []
have = -1
interval_start = -1
for i in range(len(mp)):
if mp[i] != 0:
if interval_start == -1:
interval_start = i
have = max(mp[i] - 1, have)
if have == i:
res.append([interval_start, have])
have = -1
interval_start = -1
if interval_start != -1:
res.append([interval_start, have])
return resTime & Space Complexity
- Time complexity:
- Space complexity:
Where is the length of the array and is the maximum start value among all the intervals.
Common Pitfalls
Using Strict Inequality for Overlap Check
Two intervals overlap when current.start <= last.end, not <. Intervals [1, 3] and [3, 5] are considered overlapping and should merge to [1, 5].
Forgetting to Update the End When Merging
When merging overlapping intervals, the new end should be max(last.end, current.end), not just current.end. An earlier interval might extend further than a later-starting one.
Not Sorting Before Processing
The sorting approach assumes intervals are processed in order of start time. Without sorting, you cannot guarantee that overlapping intervals are adjacent, leading to missed merges.
Sign in to join the discussion