Merge Overlapping Intervals in DSA Java - Time & Space Complexity
We want to know how the time to merge overlapping intervals grows as we add more intervals.
How does the number of intervals affect the work done to combine them?
Analyze the time complexity of the following code snippet.
import java.util.*;
public List<int[]> mergeIntervals(List<int[]> intervals) {
Collections.sort(intervals, (a, b) -> a[0] - b[0]);
List<int[]> merged = new ArrayList<>();
for (int[] interval : intervals) {
if (merged.isEmpty() || merged.get(merged.size() - 1)[1] < interval[0]) {
merged.add(interval);
} else {
merged.get(merged.size() - 1)[1] = Math.max(merged.get(merged.size() - 1)[1], interval[1]);
}
}
return merged;
}
This code sorts intervals by start time, then merges overlapping ones into a new list.
- Primary operation: Sorting the list of intervals and then looping through each interval once.
- How many times: Sorting takes time based on all intervals, then the loop runs once per interval.
As the number of intervals grows, sorting takes more time, and merging checks each interval once.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 log 10 + 10 = 40 operations |
| 100 | About 100 log 100 + 100 = 700 operations |
| 1000 | About 1000 log 1000 + 1000 = 10,000 operations |
Pattern observation: The time grows a bit faster than the number of intervals because of sorting, but the merging step grows linearly.
Time Complexity: O(n log n)
This means the time to merge intervals grows a little faster than the number of intervals because sorting takes extra work.
[X] Wrong: "Merging intervals is just a simple loop, so it must be O(n)."
[OK] Correct: Sorting the intervals first takes extra time, which dominates the total time, making it O(n log n).
Understanding this time complexity helps you explain why sorting is important before merging and shows you can analyze combined steps clearly.
"What if the intervals were already sorted? How would the time complexity change?"
Practice
Solution
Step 1: Understand overlapping intervals
Overlapping intervals share some common range, so merging combines them into one continuous interval.Step 2: Purpose of merging
Merging reduces multiple overlapping intervals into fewer intervals without overlaps, simplifying the list.Final Answer:
To combine intervals that overlap into a single continuous interval -> Option AQuick Check:
Merging = Combine overlaps [OK]
- Thinking merging sorts intervals by length
- Confusing merging with removing duplicates
- Assuming merging finds max end only
Solution
Step 1: Identify sorting by start time ascending
We need to sort intervals by their start value in ascending order for merging.Step 2: Check each option
Collections.sort(intervals, (a, b) -> a.start - b.start); sorts by start ascending correctly. intervals.sort((a, b) -> b.start - a.start); sorts descending. Collections.sort(intervals, (a, b) -> a.end - b.end); sorts by end, not start. intervals.sort((a, b) -> a.length() - b.length()); sorts by length, which is incorrect.Final Answer:
Collections.sort(intervals, (a, b) -> a.start - b.start); -> Option AQuick Check:
Sort by start ascending = Collections.sort(intervals, (a, b) -> a.start - b.start); [OK]
- Sorting by end time instead of start time
- Sorting in descending order
- Sorting by interval length
List<Interval> intervals = List.of(new Interval(1,3), new Interval(2,6), new Interval(8,10), new Interval(15,18));
List<Interval> merged = mergeIntervals(intervals);
for (Interval i : merged) {
System.out.print("[" + i.start + "," + i.end + "] ");
}Assume
mergeIntervals merges intervals correctly.Solution
Step 1: Identify overlapping intervals
Intervals (1,3) and (2,6) overlap, so they merge into (1,6). Others do not overlap.Step 2: List merged intervals
Merged list is (1,6), (8,10), (15,18).Final Answer:
[1,6] [8,10] [15,18] -> Option CQuick Check:
Merged intervals = [1,6] [8,10] [15,18] [OK]
- Not merging overlapping intervals
- Printing original intervals without merge
- Merging incorrectly leaving duplicates
public List<Interval> mergeIntervals(List<Interval> intervals) {
Collections.sort(intervals, (a, b) -> a.start - b.start);
List<Interval> merged = new ArrayList<>();
for (Interval interval : intervals) {
if (merged.isEmpty() || merged.get(merged.size() - 1).end < interval.start) {
merged.add(interval);
} else {
merged.get(merged.size() - 1).end = interval.end;
}
}
return merged;
}Solution
Step 1: Check merging logic
When intervals overlap, the code sets end to interval.end directly, which may be smaller than current end.Step 2: Correct approach
It should update end to max of current end and interval.end to cover full overlap.Final Answer:
It does not update the end to the max of overlapping intervals -> Option BQuick Check:
Update end = max(end, interval.end) needed [OK]
- Setting end to interval.end without max check
- Sorting by wrong field
- Adding overlapping intervals separately
[(1,10), (2,5), (6,9)]? What is the final merged list?Solution
Step 1: Analyze nested intervals
Intervals (2,5) and (6,9) are fully inside (1,10), so they overlap completely.Step 2: Merge nested intervals
Merging results in a single interval (1,10) covering all nested intervals.Final Answer:
[(1,10)] -> Option DQuick Check:
Nested intervals merge into outer interval [OK]
- Keeping nested intervals separate
- Splitting outer interval incorrectly
- Ignoring full overlap cases
Ready to prove it?
Pass the test and this pattern moves unknown
