Given an array of time intervals where arr[i] = [starti, endi], our task is to merge all the overlapping intervals into one and output the result which should have only mutually exclusive intervals.
Examples:
Input: arr[] = [[1, 3], [2, 4], [6, 8], [9, 10]]
Output: [[1, 4], [6, 8], [9, 10]]
Explanation: In the given intervals, we have only two overlapping intervals [1, 3] and [2, 4]. Therefore, we will merge these two and return [[1, 4]], [6, 8], [9, 10]].Input: arr[] = [[7, 8], [1, 5], [2, 4], [4, 6]]
Output: [[1, 6], [7, 8]]
Explanation: We will merge the overlapping intervals [[1, 5], [2, 4], [4, 6]] into a single interval [1, 6].
Table of Content
[Naive Approach] Checking All Possible Overlaps – O(n^2) Time and O(n) Space
A simple approach is to group all the intervals by sorting them then start from the first interval and compare it with all other intervals for overlaps. If the first interval overlaps with any other interval, then remove the other interval from the list and merge the other into the first interval. Repeat the same steps for the remaining intervals after the first.
#include <bits/stdc++.h>
using namespace std;
vector<vector<int>> mergeOverlap(vector<vector<int>> &arr) {
int n = arr.size();
sort(arr.begin(), arr.end());
vector<vector<int>> res;
// Checking for all possible overlaps
for (int i = 0; i < n; i++) {
int start = arr[i][0];
int end = arr[i][1];
// Skipping already merged intervals
if (!res.empty() && res.back()[1] >= end)
continue;
// Find the end of the merged range
for (int j = i + 1; j < n; j++) {
if (arr[j][0] <= end)
end = max(end, arr[j][1]);
}
res.push_back({start, end});
}
return res;
}
int main() {
vector<vector<int>> arr = {{7, 8}, {1, 5}, {2, 4}, {4, 6}};
vector<vector<int>> res = mergeOverlap(arr);
for (auto interval : res)
cout << interval[0] << " " << interval[1] << endl;
return 0;
}
import java.util.ArrayList;
import java.util.Arrays;
class GFG {
static ArrayList<ArrayList<Integer>> mergeOverlap(int[][] arr) {
int n = arr.length;
Arrays.sort(arr, (a, b) -> Integer.compare(a[0], b[0]));
ArrayList<ArrayList<Integer>> res = new ArrayList<>();
// Checking for all possible overlaps
for (int i = 0; i < n; i++) {
int start = arr[i][0];
int end = arr[i][1];
// Skipping already merged intervals
if (!res.isEmpty() && res.get(res.size() - 1).get(1) >= end) {
continue;
}
// Find the end of the merged range
for (int j = i + 1; j < n; j++) {
if (arr[j][0] <= end) {
end = Math.max(end, arr[j][1]);
}
}
ArrayList<Integer> interval = new ArrayList<>();
interval.add(start);
interval.add(end);
res.add(interval);
}
return res;
}
public static void main(String[] args) {
int[][] arr = {{7, 8}, {1, 5}, {2, 4}, {4, 6}};
ArrayList<ArrayList<Integer>> res = mergeOverlap(arr);
for (ArrayList<Integer> interval : res) {
System.out.println(interval.get(0) + " " + interval.get(1));
}
}
}
def mergeOverlap(arr):
n = len(arr)
arr.sort()
res = []
# Checking for all possible overlaps
for i in range(n):
start = arr[i][0]
end = arr[i][1]
# Skipping already merged intervals
if res and res[-1][1] >= end:
continue
# Find the end of the merged range
for j in range(i + 1, n):
if arr[j][0] <= end:
end = max(end, arr[j][1])
res.append([start, end])
return res
if __name__ == "__main__":
arr = [[7, 8], [1, 5], [2, 4], [4, 6]]
res = mergeOverlap(arr)
for interval in res:
print(interval[0], interval[1])
using System;
using System.Collections.Generic;
class GFG {
static List<List<int>> mergeOverlap(int[][] arr) {
int n = arr.Length;
Array.Sort(arr, (a, b) => a[0].CompareTo(b[0]));
List<List<int>> res = new List<List<int>>();
// Checking for all possible overlaps
for (int i = 0; i < n; i++) {
int start = arr[i][0];
int end = arr[i][1];
// Skipping already merged intervals
if (res.Count > 0 && res[res.Count - 1][1] >= end)
continue;
// Find the end of the merged range
for (int j = i + 1; j < n; j++) {
if (arr[j][0] <= end)
end = Math.Max(end, arr[j][1]);
}
res.Add(new List<int> { start, end });
}
return res;
}
static void Main() {
int[][] arr = new int[][] {
new int[] { 7, 8 },
new int[] { 1, 5 },
new int[] { 2, 4 },
new int[] { 4, 6 }
};
List<List<int>> res = mergeOverlap(arr);
foreach (var interval in res)
Console.WriteLine($"{interval[0]} {interval[1]}");
}
}
function mergeOverlap(arr) {
let n = arr.length;
arr.sort((a, b) => a[0] - b[0]);
let res = [];
// Checking for all possible overlaps
for (let i = 0; i < n; i++) {
let start = arr[i][0];
let end = arr[i][1];
// Skipping already merged intervals
if (res.length > 0 && res[res.length - 1][1] >= end) {
continue;
}
// Find the end of the merged range
for (let j = i + 1; j < n; j++) {
if (arr[j][0] <= end) {
end = Math.max(end, arr[j][1]);
}
}
res.push([start, end]);
}
return res;
}
const arr = [[7, 8], [1, 5], [2, 4], [4, 6]];
const res = mergeOverlap(arr);
for (const interval of res)
console.log(interval[0], interval[1]);
Output
1 6 7 8
[Expected Approach] Checking Last Merged Interval – O(n*log(n)) Time and O(n) Space
In the previous approach, for each range we are checking for possible overlaps by iterating over all the remaining ranges till the end. We can optimize this by checking only those intervals that overlap with the last merged interval. Since the intervals will be sorted based on starting point, so if we encounter an interval whose starting time lies outside the last merged interval, then all further intervals will also lie outside it.
- Sort the intervals based on their starting points so that overlapping intervals appear consecutively.
- Iterate through the sorted intervals while maintaining the last merged interval.
- If the current interval overlaps with the last merged interval, merge them by updating the ending point.
- Otherwise, append the last merged interval to the result and start a new merged interval with the current interval.
#include <bits/stdc++.h>
using namespace std;
vector<vector<int>> mergeOverlap(vector<vector<int>>& arr) {
// Sort intervals based on start values
sort(arr.begin(), arr.end());
vector<vector<int>> res;
res.push_back(arr[0]);
for (int i = 1; i < arr.size(); i++) {
vector<int>& last = res.back();
vector<int>& curr = arr[i];
// If current interval overlaps with the last merged
// interval, merge them
if (curr[0] <= last[1])
last[1] = max(last[1], curr[1]);
else
res.push_back(curr);
}
return res;
}
int main() {
vector<vector<int>> arr = {{7, 8}, {1, 5}, {2, 4}, {4, 6}};
vector<vector<int>> res = mergeOverlap(arr);
for (vector<int>& interval: res)
cout << interval[0] << " " << interval[1] << endl;
return 0;
}
import java.util.ArrayList;
import java.util.Arrays;
class GFG {
static ArrayList<ArrayList<Integer>> mergeOverlap(int[][] arr) {
// Sort intervals based on start values
Arrays.sort(arr, (a, b) -> Integer.compare(a[0], b[0]));
ArrayList<ArrayList<Integer>> res = new ArrayList<>();
ArrayList<Integer> first = new ArrayList<>();
first.add(arr[0][0]);
first.add(arr[0][1]);
res.add(first);
for (int i = 1; i < arr.length; i++) {
ArrayList<Integer> last = res.get(res.size() - 1);
int[] curr = arr[i];
// If current interval overlaps with the last merged interval,
// merge them
if (curr[0] <= last.get(1))
last.set(1, Math.max(last.get(1), curr[1]));
else {
ArrayList<Integer> interval = new ArrayList<>();
interval.add(curr[0]);
interval.add(curr[1]);
res.add(interval);
}
}
return res;
}
public static void main(String[] args) {
int[][] arr = {{7, 8}, {1, 5}, {2, 4}, {4, 6}};
ArrayList<ArrayList<Integer>> res = mergeOverlap(arr);
for (ArrayList<Integer> interval : res)
System.out.println(interval.get(0) + " " + interval.get(1));
}
}
def mergeOverlap(arr):
# Sort intervals based on start values
arr.sort()
res = []
res.append(arr[0])
for i in range(1, len(arr)):
last = res[-1]
curr = arr[i]
# If current interval overlaps with the last merged
# interval, merge them
if curr[0] <= last[1]:
last[1] = max(last[1], curr[1])
else:
res.append(curr)
return res
if __name__ == "__main__":
arr = [[7, 8], [1, 5], [2, 4], [4, 6]]
res = mergeOverlap(arr)
for interval in res:
print(interval[0], interval[1])
using System;
using System.Collections.Generic;
class GFG {
static List<List<int>> mergeOverlap(int[][] arr) {
// Sort intervals based on start values
Array.Sort(arr, (a, b) => a[0].CompareTo(b[0]));
List<List<int>> res = new List<List<int>>();
res.Add(new List<int> { arr[0][0], arr[0][1] });
for (int i = 1; i < arr.Length; i++) {
List<int> last = res[res.Count - 1];
int[] curr = arr[i];
// If current interval overlaps with last, merge them
if (last[1] >= curr[0]) {
last[1] = Math.Max(last[1], curr[1]);
} else {
res.Add(new List<int> { curr[0], curr[1] });
}
}
return res;
}
static void Main() {
int[][] arr = {
new int[] {7, 8},
new int[] {1, 5},
new int[] {2, 4},
new int[] {4, 6}
};
List<List<int>> merged = mergeOverlap(arr);
foreach (List<int> interval in merged) {
Console.WriteLine(interval[0] + " " + interval[1]);
}
}
}
function mergeOverlap(arr) {
if (arr.length === 0) return [];
// Sort intervals based on start values
const sorted = [...arr].sort((a, b) => a[0] - b[0]);
const res = [];
res.push(sorted[0]);
for (let i = 1; i < sorted.length; i++) {
const last = res[res.length - 1];
const curr = sorted[i];
// If current interval overlaps with last, merge them
if (curr[0] <= last[1]) {
last[1] = Math.max(last[1], curr[1]);
} else {
res.push(curr);
}
}
return res;
}
// Driver Code
const arr = [[7, 8], [1, 5], [2, 4], [4, 6]];
const res = mergeOverlap(arr);
for (const interval of res) {
console.log(interval[0] + " " + interval[1]);
}
Output
1 6 7 8