# 56. Merge Intervals

## Description

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

## Solutions

Solution 1: Sorting + One-pass Traversal

We can sort the intervals in ascending order by the left endpoint, and then traverse the intervals for merging operations.

The specific merging operation is as follows.

First, we add the first interval to the answer. Then, we consider each subsequent interval in turn:

• If the right endpoint of the last interval in the answer array is less than the left endpoint of the current interval, it means that the two intervals will not overlap, so we can directly add the current interval to the end of the answer array;
• Otherwise, it means that the two intervals overlap. We need to use the right endpoint of the current interval to update the right endpoint of the last interval in the answer array, setting it to the larger of the two.

Finally, we return the answer array.

The time complexity is $O(n \times \log n)$, and the space complexity is $O(\log n)$. Here, $n$ is the number of intervals.

### Example Input

Consider the following list of intervals: [[1,3],[2,6],[8,10],[15,18]]. These intervals represent parts of a line with start and end points.

1. Sort Intervals: First, the code sorts the intervals based on their start values. For our example, the intervals are already sorted, so the sorted list remains [[1,3],[2,6],[8,10],[15,18]].

2. Initialization: An empty list ans is initialized to store the merged intervals.

3. Iteration and Merging:

• The code iterates through the sorted intervals.
• For the first interval [1,3], ans is empty, so the interval is directly appended to ans.
• Next interval, [2,6], overlaps with [1,3] since 3 >= 2. So, we merge them by updating the end of the last interval in ans to max(3, 6) = 6. Now, ans = [[1,6]].
• The next interval [8,10] does not overlap with [1,6] because 6 < 8. So, [8,10] is appended to ans, resulting in ans = [[1,6], [8,10]].
• Similarly, [15,18] doesn’t overlap with the last interval in ans ([8,10]), so it’s also appended to ans. The final ans is [[1,6], [8,10], [15,18]].

The function returns the merged intervals: [[1,6],[8,10],[15,18]]. This means we have three ranges where the intervals overlap or are continuous without gaps.

• class Solution {
public int[][] merge(int[][] intervals) {
Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
List<int[]> ans = new ArrayList<>();
for (int i = 1; i < intervals.length; ++i) {
int s = intervals[i][0], e = intervals[i][1];
if (ans.get(ans.size() - 1)[1] < s) {
} else {
ans.get(ans.size() - 1)[1] = Math.max(ans.get(ans.size() - 1)[1], e);
}
}
return ans.toArray(new int[ans.size()][]);
}
}

• class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
sort(intervals.begin(), intervals.end());
vector<vector<int>> ans;
ans.emplace_back(intervals[0]);
for (int i = 1; i < intervals.size(); ++i) {
if (ans.back()[1] < intervals[i][0]) {
ans.emplace_back(intervals[i]);
} else {
ans.back()[1] = max(ans.back()[1], intervals[i][1]);
}
}
return ans;
}
};

• # Definition for an interval.
# class Interval(object):
#     def __init__(self, s=0, e=0):
#         self.start = s
#         self.end = e

class Solution(object):
def merge(self, intervals):
"""
:type intervals: List[Interval]
:rtype: List[Interval]
"""
ans = []
for intv in sorted(intervals, key=lambda x: x.start):
if ans and ans[-1].end >= intv.start:
ans[-1].end = max(ans[-1].end, intv.end)
else:
ans.append(intv)
return ans

######

'''
>>> intervals = [[111,222],[1,3],[2,6],[8,10],[15,18]]
>>> intervals.sort()
>>> intervals
[[1, 3], [2, 6], [8, 10], [15, 18], [111, 222]]
'''
class Solution:
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
intervals.sort() # default sort also ok
ans = [intervals[0]]
for s, e in intervals[1:]:
if ans[-1][1] < s:
ans.append([s, e])
else:
ans[-1][1] = max(ans[-1][1], e)
return ans

• func merge(intervals [][]int) (ans [][]int) {
sort.Slice(intervals, func(i, j int) bool { return intervals[i][0] < intervals[j][0] })
ans = append(ans, intervals[0])
for _, e := range intervals[1:] {
if ans[len(ans)-1][1] < e[0] {
ans = append(ans, e)
} else {
ans[len(ans)-1][1] = max(ans[len(ans)-1][1], e[1])
}
}
return
}

• function merge(intervals: number[][]): number[][] {
intervals.sort((a, b) => a[0] - b[0]);
const ans: number[][] = [intervals[0]];
for (let i = 1; i < intervals.length; ++i) {
if (ans.at(-1)[1] < intervals[i][0]) {
ans.push(intervals[i]);
} else {
ans.at(-1)[1] = Math.max(ans.at(-1)[1], intervals[i][1]);
}
}
return ans;
}

• public class Solution {
public int[][] Merge(int[][] intervals) {
intervals = intervals.OrderBy(a => a[0]).ToArray();
var ans = new List<int[]>();
for (int i = 1; i < intervals.Length; ++i) {
if (ans[ans.Count - 1][1] < intervals[i][0]) {
} else {
ans[ans.Count - 1][1] = Math.Max(ans[ans.Count - 1][1], intervals[i][1]);
}
}
return ans.ToArray();
}
}

• impl Solution {
pub fn merge(mut intervals: Vec<Vec<i32>>) -> Vec<Vec<i32>> {
intervals.sort_unstable_by(|a, b| a[0].cmp(&b[0]));
let n = intervals.len();
let mut res = vec![];
let mut i = 0;
while i < n {
let l = intervals[i][0];
let mut r = intervals[i][1];
i += 1;
while i < n && r >= intervals[i][0] {
r = r.max(intervals[i][1]);
i += 1;
}
res.push(vec![l, r]);
}
res
}
}