Good evening! Here's our prompt for today.
This is a classic interview question that is used in a lot in technical interviews, frequently in the form of a calendar or meeting management application. As such, we'll also assume the same use case, as it's easier to visualize.

Suppose we're given a few arrays, like [1, 4], [2, 5], [7, 10], [12, 16]
, representing time intervals. The elements within the arrays represent a start time and end time of a meeting. So an array "time range" of [1, 4]
would mean that the event starts at 1
-o'clock and ends at 4
-o'clock.
Now, what if we wanted to merge any overlapping meetings? If one meeting runs into the time of another, we'll want to combine them into one. As such, [1, 4]
and [2, 5]
, who overlap between 4 o'clock and 5 o'clock, would be combined into [1, 5]
.
For the above example of [1, 4], [2, 5], [7, 10], [12, 16]
, we would want to return [1, 5], [7, 10], [12, 16]
. Can you write a method that will do this?
Constraints
- Size of array
ranges
<=100000
- The array will always contain integer values between
-1000000000
and1000000000
- Expected time complexity :
O(n)
- Expected space complexity :
O(n)
Try to solve this here or in Interactive Mode.
How do I practice this challenge?
xxxxxxxxxx
'PASSED: `mergeIntervals([[1, 4], [2, 5], [7, 10], [12, 16]])` should return `[[1, 5], [7, 10], [12, 16]]`'
var assert = require('assert');
​
function mergeIntervals(ranges) {
// return ranges
return result;
}
​
try {
assertIsFunction(mergeIntervals, '`mergeIntervals` is a function');
​
console.log('PASSED: ' + '`mergeIntervals` is a function');
} catch (err) {
console.log(err);
}
​
try {
assert.deepEqual(
mergeIntervals([
[1, 4],
[2, 5],
[7, 10],
[12, 16],
]),
[
[1, 5],
[7, 10],
[12, 16],
]
);
Tired of reading? Watch this video explanation!
To change the speed of the video or see it in full screen, click the icons to the right of the progress bar.

Here's how we would solve this problem...
How do I use this guide?
Demystifying the Merge: A Deep Dive into Merging Time Ranges
The Initial Hurdle: Perceived Complexity
The process of merging time ranges can appear daunting at first, mostly due to misconceptions about how the merging unfolds. Yet, in reality, there's a clear workflow to follow. To build intuition, it's helpful to start with the most simplified example and dissect it step by step.
Setting the Stage: A Simple Example
Imagine we are given just two time ranges to merge: [1, 4]
and [2, 5]
. It's evident that these ranges overlap, but how exactly should we merge them?
Finding the Start Time
We observe that 1
precedes 2
. So, the merged range must start at the earliest time, which in this case is 1
. It's like having two runners on a track; naturally, the one who starts first sets the pace. This choice is relatively straightforward if you sort the intervals by their start times.
Deciding the End Time
Deciding the end time is a bit more nuanced. While 4
comes before 5
, we want our merged range to encapsulate all overlapping time. So, the end time must be 5
. Think of this as ensuring the last runner crosses the finish line.
The Backbone: Sorting as a First Step
Sorting the ranges helps us establish a chronological order, making it easier to merge them. The lowest starting time will always be the beginning of our merged range, and from there, we can focus on the end times. Here's how to sort these ranges in both JavaScript and Python:
1const ranges = [[1, 4], [2, 5]];
2
3ranges.sort(function(a, b) {
4 return a[0] - b[0] || a[1] - b[1];
5});
A Roadmap for Merging Time Ranges
Merging time ranges is a task that becomes much more manageable once you understand the underlying structure:
- Sort the Ranges: Establish a chronological order.
- Determine the Start Time: Choose the earliest start time among overlapping intervals.
- Decide the End Time: Make sure to cover all time within the overlapping intervals.
By following this methodical approach, you'll find that what seemed like a challenging problem is, in fact, quite solvable.
The Anatomy of Merging: Scenarios, Steps, and Solution Complexity
A Closer Look at Merge Scenarios
Before diving further into the code, let's acquaint ourselves with the various situations where merging becomes necessary. For a detailed understanding, you can explore all possible merging scenarios here.
Initialize Result Storage
Once our ranges are sorted, we start with an empty result
array to store our final merged intervals.
Iterating and Merging: A Walkthrough
After sorting by the starting time, we have our array looking like [[1, 4], [2, 5], [7, 10], [12, 16]]
. Now comes the fun part — iterating through these intervals and deciding when to merge.
Determining Overlap via Code
We'll iterate through our sorted ranges. To do this efficiently, we maintain a variable called last
, which tracks the last interval we've encountered. As we go through each range r
in ranges
, we'll need to decide whether to extend last
or append a new range to our result
. Here's how:
- Checking for Overlap: If the start time of
r
(r[0]
) is less than or equal to the end time oflast
(last[1]
), we have an overlap. - Extending the Interval: In the case of an overlap, we update
last[1]
to be the maximum betweenr[1]
andlast[1]
. This ensures thatlast
covers the entirety of the overlapping period.
Identifying Clear Separations
If there is no overlap — i.e., a "clear separation" exists — we simply append the new range to our result
. A clear separation is evident when last[1]
is less than r[0]
. In such a case, r
starts strictly after last
ends, and hence no merge is necessary.
xxxxxxxxxx
const ranges = [
[1, 4],
[2, 5],
];
​
const result = [];
let last;
ranges.forEach(function (r) {
// compare this new range to the last one we tracked
// if this range has a time gap before the next event, add to results
if (!last || r[0] > last[1]) {
last = r;
result.push(last);
} else if (r[1] > last[1]) {
// otherwise if its end time is longer than the last,
// extend the last range's end time to encompass this interval too
last[1] = r[1];
}
});
​
console.log(result);
Complexity Breakdown
Given that the input intervals are already sorted, our solution exhibits a linear time complexity of (O(n)) and linear space complexity. This is because we iterate through each of the (n) intervals exactly once.
The Final Logic: A Code Snapshot
The final logic could be understood as below (actual code not shown here). While iterating through the sorted array:
- Check for overlap with the
last
interval. - Extend
last
if there's an overlap. - Append to
result
in case of a clear separation.
By following this systematic approach, merging time intervals becomes a straightforward task, completely doable within reasonable time and space constraints.
Merging Intervals: A Detailed Walkthrough
Let's get our hands dirty by stepping through the actual code to merge the given list of intervals: [[1, 4], [2, 5], [7, 10], [12, 16]]
.
Preparing the Ground: Initial Sort and Setup
Before jumping into merging, we sort our intervals by their start times.
1intervals = [[1, 4], [2, 5], [7, 10], [12, 16]]
2intervals.sort(key=lambda x: x[0])
Also, initialize an empty array called result
to store the merged intervals.
1result = []
Step 1: Seed the result
Array
We start by taking the first interval [1, 4]
from our sorted array and appending it to result
.
1result.append(intervals[0])
At this point, result = [[1, 4]]
.
Step 2: Enter the Loop: First Iteration
Now, we loop through the remaining intervals in the sorted array, starting with [2, 5]
.
1current = [2, 5]
2last = result[-1] # [1, 4]
Here, last
is [1, 4]
, and current
is [2, 5]
. We notice that current[0] <= last[1]
, which indicates an overlap. So, we extend last
to cover current
.
1if current[0] <= last[1]:
2 last[1] = max(current[1], last[1]) # max(5, 4) = 5
After this step, result = [[1, 5]]
.
Step 3: Second Iteration
Next, we consider the interval [7, 10]
.
1current = [7, 10]
2last = result[-1] # [1, 5]
Here, current[0] > last[1]
, meaning there's a gap. So, we simply append current
to result
.
1result.append(current)
Now, result = [[1, 5], [7, 10]]
.
Step 4: Final Iterations
We repeat the above steps for [12, 16]
. Since it doesn't overlap with the last interval [7, 10]
, we add it to the result as is.
1result.append([12, 16])
Our final result
array becomes [[1, 5], [7, 10], [12, 16]]
.
And there you have it! We've successfully merged the intervals using Python code. This step-by-step walkthrough should give you a detailed understanding of how interval merging works in practice.

One Pager Cheat Sheet
- We can write a method that merges any overlapping meetings in a given array of time intervals with a time complexity of
O(n)
and a space complexity ofO(n)
. - Merging time ranges is relatively straightforward, as long as the intervals are properly sorted; we just need to take the smallest beginning/start time, and follow the chronological order to find the largest end/finishing time.
- We
iterate
through thesorted array
of intervals,comparing
thelast interval
andreferencing
aresult
list to store modified intervals from clear separations or overlaps.
This is our final solution.
To visualize the solution and step through the below code, click Visualize the Solution on the right-side menu or the VISUALIZE button in Interactive Mode.
xxxxxxxxxx
}
var assert = require('assert');
​
function mergeIntervals(ranges) {
ranges.sort(function (a, b) {
return a[0] - b[0] || a[1] - b[1];
});
​
let result = [],
last;
​
ranges.forEach(function (r) {
if (!last || r[0] > last[1]) {
last = r;
result.push(last);
} else if (r[1] > last[1]) last[1] = r[1];
});
​
return result;
}
​
// console.log(mergeIntervals([[1, 3], [2, 9], [3, 10], [1, 16]]))
​
try {
assertIsFunction(mergeIntervals, '`mergeIntervals` is a function');
​
console.log('PASSED: ' + '`mergeIntervals` is a function');
} catch (err) {
console.log(err);
}
​
try {
assert.deepEqual(
Great job getting through this. Let's move on.
If you had any problems with this tutorial, check out the main forum thread here.