Merge Intervals Pattern
Overview
The merge intervals pattern is a powerful technique for solving problems involving overlapping time periods, ranges, or any data that can be represented as intervals. This pattern is frequently tested in coding interviews and has many real-world applications.
Core Concept
Each interval is represented by a start and an end point: [start, end]
6 different ways in which two intervals can relate to each other:






Interval Relationships
- No Overlap:
a.end < b.startorb.end < a.start - Overlap:
a.start <= b.end && b.start <= a.end - Complete Overlap: One interval completely contains another
- Partial Overlap: Intervals partially overlap
- Adjacent:
a.end == b.startorb.end == a.start - Identical:
a.start == b.start && a.end == b.end
When to Use This Pattern
✅ Use when you see:
- Array of intervals/ranges
- Problems involving overlapping periods
- Scheduling conflicts
- Time-based data processing
- Range queries and updates
✅ Problem indicators:
- "Merge overlapping..."
- "Find conflicts in..."
- "Schedule meetings..."
- "Remove overlapping..."
- "Insert interval..."
❌ Don't use when:
- Single interval operations
- Non-overlapping guaranteed data
- Problems requiring complex interval arithmetic
Base Approach (Merge Intervals)
Statement: https://leetcode.com/problems/merge-intervals/
Solutions
https://www.geeksforgeeks.org/merging-intervals/
Sort the intervals by startTime first (if needed)
Insert the first interval from the input list into the output list.
Traverse the input list of intervals. For each interval in the input list, we do the following:
- If the input interval is overlapping with the last interval in the output list, merge these two intervals and replace the last interval of the output list with this merged interval.
- Otherwise, add the input interval to the output list.
function merge(intervals: number[][]): number[][] {
if (intervals.length === 1) return intervals;
// 0. Sort the intervals by startTime first (if needed)
intervals.sort((a, b) => a[0] - b[0]);
// 1. Insert the first interval from the input list into the output list.
const result = [intervals[0]];
// 2.Traverse the input list of intervals. For each interval in the input list, we do the following:
for (let i = 1; i < intervals.length; i++) {
const lastInterval = result.pop();
// 2a. If the input interval is overlapping with the last interval in the output list
if (lastInterval[1] >= intervals[i][0]) {
// merge these two intervals and replace the last interval of the output list with this merged interval.
const newInterval = [lastInterval[0], Math.max(lastInterval[1], intervals[i][1])];
result.push(newInterval);
} else {
// 2b. Otherwise, add the input interval to the output list.
result.push(lastInterval); // push back
result.push(intervals[i]); // add to the last
}
}
return result;
}Complexity
- Time: O(n)
- Space: O(1) (only use constant space other than the input and output data structures)
Common Patterns and Variations
1. Insert Interval Pattern
Problem: Insert a new interval into a sorted list of non-overlapping intervals
function insert(intervals: number[][], newInterval: number[]): number[][] {
const result: number[][] = [];
let i = 0;
// Add all intervals that end before newInterval starts
while (i < intervals.length && intervals[i][1] < newInterval[0]) {
result.push(intervals[i]);
i++;
}
// Merge overlapping intervals
while (i < intervals.length && intervals[i][0] <= newInterval[1]) {
newInterval[0] = Math.min(newInterval[0], intervals[i][0]);
newInterval[1] = Math.max(newInterval[1], intervals[i][1]);
i++;
}
result.push(newInterval);
// Add remaining intervals
while (i < intervals.length) {
result.push(intervals[i]);
i++;
}
return result;
}2. Remove Overlapping Intervals
Problem: Remove minimum number of intervals to make the rest non-overlapping
function eraseOverlapIntervals(intervals: number[][]): number {
if (intervals.length <= 1) return 0;
// Sort by end time (greedy approach)
intervals.sort((a, b) => a[1] - b[1]);
let count = 0;
let lastEnd = intervals[0][1];
for (let i = 1; i < intervals.length; i++) {
if (intervals[i][0] < lastEnd) {
// Overlapping interval, remove it
count++;
} else {
lastEnd = intervals[i][1];
}
}
return count;
}3. Interval Intersection
Problem: Find intersection of two lists of intervals
function intervalIntersection(firstList: number[][], secondList: number[][]): number[][] {
const result: number[][] = [];
let i = 0, j = 0;
while (i < firstList.length && j < secondList.length) {
const start = Math.max(firstList[i][0], secondList[j][0]);
const end = Math.min(firstList[i][1], secondList[j][1]);
// Check if there's an intersection
if (start <= end) {
result.push([start, end]);
}
// Move pointer of interval that ends first
if (firstList[i][1] < secondList[j][1]) {
i++;
} else {
j++;
}
}
return result;
}4. Meeting Rooms Pattern
Problem: Determine if a person can attend all meetings
function canAttendMeetings(intervals: number[][]): boolean {
intervals.sort((a, b) => a[0] - b[0]);
for (let i = 1; i < intervals.length; i++) {
if (intervals[i][0] < intervals[i - 1][1]) {
return false; // Overlap detected
}
}
return true;
}
function minMeetingRooms(intervals: number[][]): number {
if (intervals.length === 0) return 0;
const starts = intervals.map(i => i[0]).sort((a, b) => a - b);
const ends = intervals.map(i => i[1]).sort((a, b) => a - b);
let rooms = 0;
let endPointer = 0;
for (let i = 0; i < starts.length; i++) {
if (starts[i] >= ends[endPointer]) {
endPointer++;
} else {
rooms++;
}
}
return rooms;
}Advanced Techniques
1. Sweep Line Algorithm
Use Case: Process events in chronological order
interface Event {
time: number;
type: 'start' | 'end';
}
function maxConcurrentIntervals(intervals: number[][]): number {
const events: Event[] = [];
for (const [start, end] of intervals) {
events.push({ time: start, type: 'start' });
events.push({ time: end, type: 'end' });
}
// Sort events by time, with 'end' events before 'start' events at same time
events.sort((a, b) => {
if (a.time !== b.time) return a.time - b.time;
return a.type === 'end' ? -1 : 1;
});
let maxConcurrent = 0;
let current = 0;
for (const event of events) {
if (event.type === 'start') {
current++;
maxConcurrent = Math.max(maxConcurrent, current);
} else {
current--;
}
}
return maxConcurrent;
}2. Interval Tree (for complex queries)
class IntervalTreeNode {
interval: [number, number];
max: number;
left: IntervalTreeNode | null = null;
right: IntervalTreeNode | null = null;
constructor(interval: [number, number]) {
this.interval = interval;
this.max = interval[1];
}
}
class IntervalTree {
root: IntervalTreeNode | null = null;
insert(interval: [number, number]): void {
this.root = this.insertHelper(this.root, interval);
}
private insertHelper(node: IntervalTreeNode | null, interval: [number, number]): IntervalTreeNode {
if (!node) return new IntervalTreeNode(interval);
if (interval[0] < node.interval[0]) {
node.left = this.insertHelper(node.left, interval);
} else {
node.right = this.insertHelper(node.right, interval);
}
node.max = Math.max(node.max, interval[1]);
return node;
}
search(interval: [number, number]): [number, number] | null {
return this.searchHelper(this.root, interval);
}
private searchHelper(node: IntervalTreeNode | null, interval: [number, number]): [number, number] | null {
if (!node) return null;
// Check if current interval overlaps
if (this.doOverlap(node.interval, interval)) {
return node.interval;
}
// If left subtree has overlapping interval
if (node.left && node.left.max >= interval[0]) {
return this.searchHelper(node.left, interval);
}
return this.searchHelper(node.right, interval);
}
private doOverlap(a: [number, number], b: [number, number]): boolean {
return a[0] <= b[1] && b[0] <= a[1];
}
}Problem-Solving Strategy
Step-by-Step Approach
Understand the Problem
- What type of intervals are we dealing with?
- What constitutes an overlap?
- What's the expected output?
Choose the Right Approach
- Simple merge: Sort by start time
- Optimization problems: Sort by end time (greedy)
- Query problems: Consider interval tree or sweep line
Handle Edge Cases
- Empty input
- Single interval
- No overlaps
- Complete overlaps
- Adjacent intervals
Optimize
- Can we avoid sorting?
- Can we use two pointers?
- Is there a greedy solution?
Interview Tips
Common Mistakes
- Incorrect Overlap Detection: Remember
a.start <= b.end && b.start <= a.end - Wrong Sorting Strategy: Choose based on problem requirements
- Edge Case Handling: Empty arrays, single intervals
- Off-by-One Errors: Be careful with inclusive/exclusive endpoints
Optimization Techniques
- Greedy Approach: For optimization problems, sort by end time
- Two Pointers: For intersection problems
- Sweep Line: For complex event processing
- Early Termination: Stop when no more overlaps possible
Time Complexity Analysis
- Sorting: O(n log n)
- Merging: O(n)
- Overall: Usually O(n log n) due to sorting
- Space: O(1) to O(n) depending on output requirements
Problem Implementations
This directory contains the following problem solutions:
Problem Directories
- Employee Free Time - Find common free time slots across employees
- Word Search - Word search problem using interval concepts
Resources
- Images - Visual diagrams and illustrations for interval problems
Practice Problems
Easy
Medium
Hard
Real-World Applications
- Calendar Systems: Meeting scheduling, conflict detection
- Resource Management: Room booking, equipment allocation
- Network Traffic: Bandwidth allocation, QoS management
- Database Systems: Range queries, index optimization
- Computational Geometry: Line segment intersection
- Bioinformatics: Gene sequence analysis, protein folding
Related Patterns
- Sweep Line Algorithm: For event-based processing
- Two Pointers: For intersection problems
- Greedy Algorithms: For optimization problems
- Binary Search: For interval queries in sorted data