5. Merge Overlapping Sub-intervals
Source: Merge Overlapping Sub-intervals
Problem Statement
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] are overlapping we can merge them to form [1,6] intervals.
Approach 1: Brute-Force Approach
The main idea is to combine intervals that overlap with each other. To do this easily, we first sort the intervals by their starting point so that all overlapping intervals come next to each other. Then, for each interval, we try to see if the next ones overlap with it. If they do, we merge them into one bigger interval. We keep doing this until we find a non-overlapping interval, and then start the process again from that point.
-
Sort all intervals based on their starting points. This helps bring all overlapping intervals next to each other.
-
Go through each interval one by one and if the current interval is already covered by a previously merged interval, skip it. Else, pick the current interval as the starting point of a new merged interval.
-
Now run another loop to check if the following intervals overlap with the current one
-
If the start of next interval is less than or equal to the end of the current merged interval, it means they overlap. Therefore, extend the end of the merged interval to be the maximum of the two ends.
-
Keep doing this for the next intervals as long as they overlap. As soon as you find an interval that doesn't overlap, break the inner loop and move back to the outer loop to process the next non-overlapping interval.
-
Store each merged interval in the final answer list and after the loop ends, return the list of merged intervals.
-
Complexity: Time Complexity: O(N^2), for every interval we check all future intervals.
Space Complexity: ON), additonal space used to store the non-overlapping intervals.
Solutions
- C++
- Java
- Python
- JavaScript
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
// Function to merge overlapping intervals using brute force
vector<vector<int>> merge(vector<vector<int>>& intervals) {
// Sort intervals based on start time
sort(intervals.begin(), intervals.end());
// Result array to store merged intervals
vector<vector<int>> ans;
// Loop through each interval
int n = intervals.size();
for (int i = 0; i < n; ) {
// Start of current merged interval
int start = intervals[i][0];
int end = intervals[i][1];
// Merge with all overlapping intervals
int j = i + 1;
while (j < n && intervals[j][0] <= end) {
// Update end to the maximum of current end and overlapping interval's end
end = max(end, intervals[j][1]);
j++;
}
// Add the merged interval to result
ans.push_back({start, end});
// Move to the next non-overlapping interval
i = j;
}
return ans;
}
};
int main() {
Solution sol;
vector<vector<int>> intervals = {{1,3}, {2,6}, {8,10}, {15,18}};
vector<vector<int>> result = sol.merge(intervals);
for (auto interval : result) {
cout << "[" << interval[0] << "," << interval[1] << "] ";
}
return 0;
}
import java.util.*;
class Solution {
// Function to merge overlapping intervals using brute force
public List<List<Integer>> merge(int[][] intervals) {
// Sort intervals based on starting point
Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
List<List<Integer>> ans = new ArrayList<>();
int n = intervals.length;
int i = 0;
// Loop through intervals
while (i < n) {
// Start of merged interval
int start = intervals[i][0];
int end = intervals[i][1];
int j = i + 1;
// Check all overlapping intervals
while (j < n && intervals[j][0] <= end) {
// Extend the end of current interval
end = Math.max(end, intervals[j][1]);
j++;
}
// Add merged interval to result
ans.add(Arrays.asList(start, end));
// Move to next non-overlapping interval
i = j;
}
return ans;
}
}
public class Main {
public static void main(String[] args) {
Solution sol = new Solution();
int[][] intervals = {{1, 3}, {2, 6}, {8, 10}, {15, 18}};
List<List<Integer>> result = sol.merge(intervals);
for"" (List<Integer> interval : result) {
System.out.print(interval + " ");
}
}
}
from typing import List
class Solution:
# Function to merge overlapping intervals using brute force
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
# Sort the intervals based on the start time
intervals.sort()
ans = []
n = len(intervals)
i = 0
# Loop through each interval
while i < n:
# Take current interval's start and end
start = intervals[i][0]
end = intervals[i][1]
j = i + 1
# Merge with all intervals that overlap
while j < n and intervals[j][0] <= end:
# Extend the end if overlapping
end = max(end, intervals[j][1])
j += 1
# Append merged interval to result
ans.append([start, end])
# Move to the next non-overlapping interval
i = j
return ans
# Driver code
sol = Solution()
intervals = [[1,3],[2,6],[8,10],[15,18]]
print(sol.merge(intervals))
class Solution {
// Function to merge overlapping intervals using brute force
merge(intervals) {
// Sort intervals by starting point
intervals.sort((a, b) => a[0] - b[0]);
const ans = [];
let i = 0;
const n = intervals.length;
// Loop through all intervals
while (i < n) {
// Take current interval's start and end
let start = intervals[i][0];
let end = intervals[i][1];
let j = i + 1;
// Merge overlapping intervals
while (j < n && intervals[j][0] <= end) {
// Extend the end as needed
end = Math.max(end, intervals[j][1]);
j++;
}
// Push merged interval to result
ans.push([start, end]);
// Move to next non-overlapping interval
i = j;
}
return ans;
}
}
// Driver code
const sol = new Solution();
const intervals = [[1, 3], [2, 6], [8, 10], [15, 18]];
console.log(sol.merge(intervals));
Approach 2: Optimal Approach
Imagine laying intervals out on a number line. If two intervals overlap, we can combine them into one, like merging blocks that touch or overlap.
Instead of checking each interval with every other one (as in brute-force), we first sort the intervals, so that any overlapping intervals will come one after the other. This way, we only need to compare each interval with the last one added to our answer. If they overlap, we merge them. If they don’t, we simply add the current interval as a new entry.
-
Sort the intervals based on their starting points. This ensures overlapping intervals come together.
-
Initialize an empty list to store the final merged intervals.
-
If the list is empty or the current interval starts after the last one ends, it means there is no overlap, so just add it to the list.
-
If the current interval starts before or exactly at the end of the last one, it means there is overlap. So, combine both by extending the end of the last one to the further end of the two.
-
Keep doing this until all intervals have been checked. The final list will now contain only non-overlapping, merged intervals.
-
Complexity: Time Complexity: O(N*logN) + O(N), we sort the entire array and then merge them in a single pass.
Space Complexity: ON), additonal space used to store the non-overlapping intervals.
Solutions
- C++
- Java
- Python
- JavaScript
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
// Function to merge overlapping intervals
vector<vector<int>> merge(vector<vector<int>>& intervals) {
// Sort intervals based on starting time
sort(intervals.begin(), intervals.end());
// Vector to store final merged intervals
vector<vector<int>> merged;
// Traverse each interval
for (auto interval : intervals) {
// If merged is empty or current interval does not overlap
if (merged.empty() || merged.back()[1] < interval[0]) {
// Add current interval as a new non-overlapping block
merged.push_back(interval);
} else {
// Overlapping: merge by extending the end time
merged.back()[1] = max(
merged.back()[1],
interval[1]
);
}
}
return merged;
}
};
int main() {
Solution sol;
vector<vector<int>> intervals = {
{1, 3}, {2, 6}, {8, 10}, {15, 18}
};
vector<vector<int>> result = sol.merge(intervals);
for (auto v : result) {
cout << "[" << v[0] << "," << v[1] << "] ";
}
return 0;
}
import java.util.*;
class Solution {
// Function to merge overlapping intervals
public List<List<Integer>> merge(int[][] intervals) {
// Sort intervals based on start time
Arrays.sort(
intervals,
(a, b) -> Integer.compare(a[0], b[0])
);
// List to store merged intervals
List<List<Integer>> merged = new ArrayList<>();
// Traverse through all intervals
for (int[] interval : intervals) {
// If merged list is empty or no overlap
if (
merged.isEmpty() ||
merged.get(merged.size() - 1).get(1) < interval[0]
) {
// Add current interval as a new block
merged.add(
Arrays.asList(interval[0], interval[1])
);
} else {
// Overlapping: update end to max of both
int last = merged.size() - 1;
int maxEnd = Math.max(
merged.get(last).get(1),
interval[1]
);
merged.get(last).set(1, maxEnd);
}
}
return merged;
}
}
class Main {
public static void main(String[] args) {
Solution sol = new Solution();
int[][] intervals = {
{1, 3}, {2, 6}, {8, 10}, {15, 18}
};
List<List<Integer>> result = sol.merge(intervals);
for (List<Integer> interval : result) {
System.out.print(
"[" + interval.get(0) + "," + interval.get(1) + "] "
);
}
}
}
from typing import List
class Solution:
# Function to merge overlapping intervals
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
# Sort intervals based on the start time
intervals.sort()
# List to store final merged intervals
merged = []
# Traverse all intervals
for interval in intervals:
# If merged list is empty or current interval doesn't overlap
if not merged or merged[-1][1] < interval[0]:
# Append current interval as a new one
merged.append(interval)
else:
# Overlapping: merge by extending the end
merged[-1][1] = max(
merged[-1][1],
interval[1]
)
return merged
# Example usage
sol = Solution()
intervals = [[1, 3], [2, 6], [8, 10], [15, 18]]
print(sol.merge(intervals))
class Solution {
// Function to merge overlapping intervals
merge(intervals) {
// Sort intervals based on starting time
intervals.sort((a, b) => a[0] - b[0]);
// Array to store final merged intervals
const merged = [];
// Traverse each interval
for (const interval of intervals) {
// If merged is empty or no overlap
if (
merged.length === 0 ||
merged[merged.length - 1][1] < interval[0]
) {
// Push new non-overlapping interval
merged.push(interval);
} else {
// Merge overlapping interval
merged[merged.length - 1][1] = Math.max(
merged[merged.length - 1][1],
interval[1]
);
}
}
return merged;
}
}
// Example usage
const sol = new Solution();
const intervals = [
[1, 3], [2, 6], [8, 10], [15, 18]
];
console.log(sol.merge(intervals));