Why Intervals Are a Common Problem Pattern in DSA Go - Performance Analysis
Intervals problems often involve sorting and merging ranges. Analyzing their time complexity helps us understand how the solution scales as the number of intervals grows.
We want to know how the number of operations changes when we handle more intervals.
Analyze the time complexity of the following code snippet.
func mergeIntervals(intervals [][]int) [][]int {
if len(intervals) == 0 {
return intervals
}
sort.Slice(intervals, func(i, j int) bool {
return intervals[i][0] < intervals[j][0]
})
merged := [][]int{intervals[0]}
for i := 1; i < len(intervals); i++ {
last := merged[len(merged)-1]
if intervals[i][0] <= last[1] {
if intervals[i][1] > last[1] {
last[1] = intervals[i][1]
}
} else {
merged = append(merged, intervals[i])
}
}
return merged
}
This code merges overlapping intervals by first sorting them and then combining where needed.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Sorting the intervals and then looping through them once.
- How many times: Sorting takes about n log n steps; the loop runs n-1 times.
As the number of intervals grows, sorting takes more time, and the single loop grows linearly.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 * log 10 + 10 ≈ 43 |
| 100 | About 100 * log 100 + 100 ≈ 700 |
| 1000 | About 1000 * log 1000 + 1000 ≈ 10,000 |
Pattern observation: The sorting dominates and grows a bit faster than just the number of intervals.
Time Complexity: O(n log n)
This means the time needed grows a little faster than the number of intervals because of sorting, but the merging step is just a simple pass.
[X] Wrong: "Merging intervals is just O(n) because we only loop once."
[OK] Correct: Sorting the intervals first takes more time, which is the main cost, so the total time is not just linear.
Understanding this pattern helps you solve many real problems involving ranges or schedules. It shows you how sorting often sets the pace for efficiency.
"What if the intervals were already sorted? How would the time complexity change?"
Practice
Solution
Step 1: Understand what intervals represent
Intervals represent a start and end range, often used to show time periods or value ranges.Step 2: Recognize why intervals are useful in scheduling
They help model overlapping events or tasks that need merging or conflict checking.Final Answer:
Because intervals represent ranges of time or values that can overlap or be merged -> Option BQuick Check:
Intervals = ranges that can overlap [OK]
- Thinking intervals are single points
- Assuming intervals never overlap
- Confusing intervals with unrelated data types
Solution
Step 1: Recall Go struct declaration syntax
In Go, structs are declared with 'type Name struct { fields }' and fields have type after name.Step 2: Check each option
type Interval struct { start int; end int } matches Go syntax exactly. struct Interval { int start; int end } uses C-style syntax. Interval := struct { start, end int } is invalid assignment. type Interval = (start int, end int) uses tuple-like syntax not in Go.Final Answer:
type Interval struct { start int; end int } -> Option AQuick Check:
Go struct syntax = type Name struct { fields } [OK]
- Using C or other language struct syntax
- Trying to assign struct to variable directly
- Using tuple or parentheses syntax
package main
import "fmt"
type Interval struct { start, end int }
func mergeIntervals(intervals []Interval) []Interval {
if len(intervals) == 0 { return intervals }
result := []Interval{}
current := intervals[0]
for i := 1; i < len(intervals); i++ {
if intervals[i].start <= current.end {
if intervals[i].end > current.end {
current.end = intervals[i].end
}
} else {
result = append(result, current)
current = intervals[i]
}
}
result = append(result, current)
return result
}
func main() {
intervals := []Interval{{1,3}, {2,6}, {8,10}, {15,18}}
merged := mergeIntervals(intervals)
for _, iv := range merged {
fmt.Printf("%d-%d ", iv.start, iv.end)
}
}Solution
Step 1: Understand merging logic
The code merges intervals if current interval start is <= previous end, extending end if needed.Step 2: Trace intervals one by one
Start with {1,3}, next {2,6} overlaps, merge to {1,6}. Next {8,10} no overlap, add {1,6} to result, current = {8,10}. Next {15,18} no overlap, add {8,10}, current = {15,18}. Finally add {15,18}.Final Answer:
1-6 8-10 15-18 -> Option AQuick Check:
Merged intervals = 1-6 8-10 15-18 [OK]
- Not updating current interval end correctly
- Appending intervals too early or late
- Confusing overlap condition
func isOverlap(a, b Interval) bool {
return a.start < b.end && b.start < a.end
}Solution
Step 1: Understand overlap condition
Two intervals overlap if one starts before the other ends, including touching edges.Step 2: Check use of '<' vs '<='
Using '<' misses cases where intervals just touch (e.g., end == start). Using '<=' includes touching as overlap.Final Answer:
The function incorrectly uses '<' instead of '<=' causing missed overlaps -> Option DQuick Check:
Overlap check needs <= to include touching intervals [OK]
- Using strict < misses edge overlaps
- Confusing start and end comparisons
- Assuming no overlap if edges touch
Solution
Step 1: Sort intervals by start time
Sorting helps process meetings in chronological order.Step 2: Use a min-heap to track earliest finishing meeting
Min-heap stores end times of ongoing meetings; if next meeting starts after earliest end, reuse room; else allocate new room.Step 3: Count rooms allocated
The heap size at any time is the number of rooms needed.Final Answer:
Sort intervals by start time, use a min-heap to track earliest end time, allocate rooms accordingly -> Option CQuick Check:
Min rooms = max concurrent meetings tracked by min-heap [OK]
- Merging intervals loses overlap info
- Dividing count by two is incorrect
- Sorting by end time alone misses overlaps
Ready to prove it?
Pass the test and this pattern moves unknown
