Merge Overlapping Intervals in DSA Rust - Time & Space Complexity
We want to understand how the time needed to merge overlapping intervals changes as the number of intervals grows.
Specifically, how does the program's work increase when we have more intervals to merge?
Analyze the time complexity of the following code snippet.
fn merge_intervals(mut intervals: Vec<(i32, i32)>) -> Vec<(i32, i32)> {
intervals.sort_by_key(|k| k.0);
let mut merged = Vec::new();
for interval in intervals {
if merged.is_empty() || merged.last().unwrap().1 < interval.0 {
merged.push(interval);
} else {
let last = merged.last_mut().unwrap();
if last.1 < interval.1 {
last.1 = interval.1;
}
}
}
merged
}
This code sorts intervals by their start time, then merges any that overlap into one combined interval.
- Primary operation: Sorting the list of intervals by start time.
- How many times: Sorting takes multiple comparisons, roughly proportional to n log n where n is the number of intervals.
- Secondary operation: One loop through all intervals to merge them.
- How many times: This loop runs exactly n times, once per interval.
As the number of intervals grows, the sorting step grows faster than the merging loop.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 * log(10) + 10 = 43 operations |
| 100 | About 100 * log(100) + 100 = 700 operations |
| 1000 | About 1000 * log(1000) + 1000 = 10,000 operations |
Pattern observation: The sorting dominates and grows faster than just the number of intervals, roughly like n times log n.
Time Complexity: O(n log n)
This means the time needed grows a bit faster than the number of intervals because sorting takes more work as the list grows.
[X] Wrong: "Merging intervals is just a simple loop, so it must be O(n)."
[OK] Correct: The sorting step before merging takes more time than the loop, so the overall time is not just O(n) but O(n log n).
Understanding this time complexity helps you explain why sorting is necessary before merging and shows you can analyze combined steps clearly.
"What if the intervals were already sorted? How would the time complexity change?"
Practice
[[1,3],[2,6],[8,10]]?Solution
Step 1: Understand overlapping intervals
Intervals overlap if one starts before the other ends. For example, [1,3] and [2,6] overlap.Step 2: Merge overlapping intervals
We combine overlapping intervals into one that covers both, like merging [1,3] and [2,6] into [1,6].Final Answer:
Combine intervals that overlap into one continuous interval -> Option CQuick Check:
Merging overlapping intervals means combining them [OK]
- Sorting only by end values misses correct merges
- Removing intervals instead of merging
- Splitting intervals unnecessarily
Solution
Step 1: Identify sorting by start value
We want to sort intervals by their first element (start), so we usesort_by_keywithinterval[0].Step 2: Check sorting direction and key
intervals.sort_by_key(|interval| interval[0]); sorts ascending by start, which is correct. intervals.sort_by(|a, b| b[0].cmp(&a[0])); sorts descending, which is wrong. intervals.sort_by_key(|interval| interval[1]); sorts by end, not start.Final Answer:
intervals.sort_by_key(|interval| interval[0]); -> Option AQuick Check:
Sort by start value using sort_by_key(interval[0]) [OK]
- Sorting by end instead of start
- Sorting descending instead of ascending
- Using sort() without key for nested vectors
let mut intervals = vec![vec![1,4], vec![2,5], vec![7,9]];
intervals.sort_by_key(|x| x[0]);
let mut merged = vec![];
for interval in intervals {
if merged.is_empty() || merged.last().unwrap()[1] < interval[0] {
merged.push(interval);
} else {
let last = merged.last_mut().unwrap();
last[1] = last[1].max(interval[1]);
}
}
println!("{:?}", merged);Solution
Step 1: Sort intervals by start
Sorted intervals: [[1,4], [2,5], [7,9]] (already sorted).Step 2: Merge overlapping intervals
[1,4] and [2,5] overlap, merged to [1,5]. [7,9] does not overlap, so added as is.Final Answer:
[[1,5], [7,9]] -> Option DQuick Check:
Merged intervals = [[1,5], [7,9]] [OK]
- Not merging overlapping intervals
- Merging non-overlapping intervals
- Incorrectly updating interval ends
let mut intervals = vec![vec![1,3], vec![2,4], vec![5,7]];
intervals.sort_by_key(|x| x[1]);
let mut merged = vec![];
for interval in intervals {
if merged.is_empty() || merged.last().unwrap()[1] < interval[0] {
merged.push(interval);
} else {
let last = merged.last_mut().unwrap();
last[1] = last[1].max(interval[1]);
}
}
println!("{:?}", merged);Solution
Step 1: Check sorting key
The code sorts intervals by their end value, but merging requires sorting by start value to process intervals in order.Step 2: Effect of wrong sorting
Sorting by end causes intervals to be processed out of order, leading to missed merges or incorrect merges.Final Answer:
Sorting by end value causes incorrect merging -> Option AQuick Check:
Sort by start, not end, for correct merging [OK]
- Sorting by end instead of start
- Misusing mutable references
- Incorrect overlap condition
[[1,3],[2,4],[5,7],[6,8],[9,10]], write a Rust function to merge all overlapping intervals and return the merged list. What is the correct merged output?Solution
Step 1: Sort intervals by start
Sorted intervals: [[1,3],[2,4],[5,7],[6,8],[9,10]] (already sorted).Step 2: Merge overlapping intervals step-by-step
Merge [1,3] and [2,4] to [1,4]. Merge [5,7] and [6,8] to [5,8]. [9,10] stays separate.Final Answer:
[[1,4],[5,8],[9,10]] -> Option BQuick Check:
Merge intervals by extending ends where overlapping [OK]
- Not merging all overlapping intervals
- Merging non-overlapping intervals
- Incorrect final intervals after merge
Ready to prove it?
Pass the test and this pattern moves unknown
