What this module covers: So far, this course has dealt with single points or contiguous runs within one sequence. This module shifts to reasoning about many ranges at once — meetings on a calendar, time periods, numeric bands — and the two techniques that make sense of them efficiently: sorting-and-merging, and sweeping through start/end events in order while tracking a running count.
Representing Intervals
An interval is just a pair — a start and an end:
typescript
The moment you have a collection of these, two questions come up constantly: which ones overlap and should be merged into one? and how many are active at the same time, at peak? Both questions have the same starting move: sort first, so overlaps and boundaries become adjacent and easy to detect in a single pass.
Merging Overlapping Intervals
Sort by start time, then walk through once: if the current interval's start is within the previous interval's range, merge them by extending the end; otherwise, start a new interval.
typescript
Sorting costs O(n log n) (Modules P-9/P-10), and the merge pass afterward is a single O(n) walk — no interval is ever compared against more than its immediate predecessor, because sorting has already guaranteed that any interval it could overlap with is sitting right next to it. Without sorting first, you'd be back to comparing every interval against every other one — O(n²).
Line Sweep: Counting Overlaps at Any Moment
A different question: what is the maximum number of intervals active at the same time? — the classic "minimum meeting rooms needed" problem. The insight is to stop thinking about intervals as units and instead think about the individual start and end events, sorted in time order, sweeping through them like a line moving left to right across a timeline.
typescript
Separating starts and ends into two sorted arrays and sweeping through both with two pointers (Module F-8) turns "how many meetings overlap at the busiest moment" into a single pass: every start event increments a running count, every end event decrements it, and the peak value the counter ever reaches is the answer. This is the line sweep technique — process discrete events in time order, maintaining a running total, rather than checking every pair of intervals for overlap directly (which would be O(n²)).
Analogy: Line sweep is like a bouncer with a clicker at a venue with no capacity limit stated up front. Every time someone walks in, click up; every time someone leaves, click down. The highest number the clicker ever shows, across the whole night, is the true peak occupancy — you never needed to cross-reference every guest against every other guest to know it.
Inserting Into an Already-Merged List
A related, common variant: given a list of intervals that's already sorted and non-overlapping, insert one new interval and merge it into place.
typescript
Because the input is already sorted, this is a single O(n) pass with no re-sort needed — the same "three zones" structure (before, overlapping, after) that a fresh insert into any sorted structure tends to fall into.
Where This Shows Up in Your Stack
Calendar and scheduling systems: detecting a double-booking is exactly mergeIntervals' overlap check; finding the minimum number of resources (rooms, servers, support agents) needed to cover a set of time-bound bookings without conflict is exactly minMeetingRooms.
Time-series data and log analysis: merging overlapping "downtime windows" from multiple monitoring sources into a single incident timeline, or computing "how many concurrent connections were open at the busiest second," both reduce to the same sort-and-sweep shape.
Resource allocation (VM scheduling, connection pool sizing, worker allocation): "what's the peak concurrent demand we need to provision for" is precisely the line-sweep counting problem — sizing a system for its peak overlap, not its average load.
Rate limiting and quota windows: a set of overlapping "active request" intervals, swept the same way, gives you a peak-concurrency number that's a more accurate capacity signal than counting total requests over a period.
Summary
Intervals are [start, end] pairs, and almost every interval problem starts by sorting them — usually by start time — so overlaps become adjacent and detectable in one pass.
Merging overlapping intervals is an O(n log n) sort followed by an O(n) single pass, extending the last merged interval whenever the next one overlaps it.
Line sweep answers "what's the peak overlap" by separating starts and ends into sorted event lists and sweeping through them with a running counter — incrementing on a start, decrementing on an end, tracking the maximum.
Both techniques replace an O(n²) all-pairs overlap check with an O(n log n) sort plus an O(n) pass, the same trade Modules F-8 and F-9 made by exploiting order instead of brute-forcing every combination.
This directly models calendar scheduling, incident-window merging, and peak-capacity planning in production systems.
The next module, Binary Search and Its Variants, moves from scanning sorted data linearly to a fundamentally faster O(log n) way of searching it — closing out Foundation before Practitioner begins.
Knowledge Check
In mergeIntervals, why is sorting the intervals by start time a required first step, rather than an optional optimization?
In minMeetingRooms, why does incrementing rooms when starts[s] < ends[e] (strictly less than, not less-than-or-equal) correctly determine that a new room is needed?
According to the module, what is the core idea behind the "line sweep" technique that makes minMeetingRooms O(n log n) instead of the O(n²) an all-pairs overlap check would require?
Test your knowledge with more question sets
Sign in to access a wider variety of questions and get notified when new practice sets are added to this module.
functionmergeIntervals(intervals: Interval[]): Interval[]{if(intervals.length ===0)return[];const sorted =[...intervals].sort((a, b)=> a[0]- b[0]);// sort by start (Modules P-9/P-10)const merged: Interval[]=[sorted[0]];for(let i =1; i < sorted.length; i++){const current = sorted[i];const last = merged[merged.length -1];if(current[0]<= last[1]){// Overlaps (or touches) the last merged interval — extend it instead of adding a new one last[1]= Math.max(last[1], current[1]);}else{ merged.push(current);}}return merged;}mergeIntervals([[1,3],[2,6],[8,10],[15,18]]);// [[1, 6], [8, 10], [15, 18]] — [1,3] and [2,6] overlap and merge; the others stand alone
functionminMeetingRooms(intervals: Interval[]):number{const starts = intervals.map(([s])=> s).sort((a, b)=> a - b);const ends = intervals.map(([, e])=> e).sort((a, b)=> a - b);let rooms =0;let maxRooms =0;let s =0, e =0;while(s < starts.length){if(starts[s]< ends[e]){ rooms++;// a meeting starts before the earliest ongoing one ends — needs another room s++;}else{ rooms--;// a meeting ends before (or as) the next one starts — free up its room e++;} maxRooms = Math.max(maxRooms, rooms);}return maxRooms;}minMeetingRooms([[0,30],[5,10],[15,20]]);// 2
functioninsertInterval(intervals: Interval[], newInterval: Interval): Interval[]{const result: Interval[]=[];let i =0;// 1. Every interval entirely before the new one — keep as-iswhile(i < intervals.length && intervals[i][1]< newInterval[0]){ result.push(intervals[i]); i++;}// 2. Every interval overlapping the new one — merge them all togetherlet merged = newInterval;while(i < intervals.length && intervals[i][0]<= merged[1]){ merged =[Math.min(merged[0], intervals[i][0]), Math.max(merged[1], intervals[i][1])]; i++;} result.push(merged);// 3. Every interval entirely after — keep as-iswhile(i < intervals.length){ result.push(intervals[i]); i++;}return result;}