Pattern Guide
Greedy Interval Problems
"Activity selection, meeting rooms, interval scheduling. Sort + greedy sweep."
Interval scheduling problems ask for optimal selection or coverage of time intervals. Core greedy patterns: activity selection (maximize non-overlapping intervals — sort by end time), interval covering (minimum intervals to cover a range — sort by start, greedily pick farthest reach), meeting rooms (minimum rooms needed — count overlapping at any moment), and task scheduling with deadlines.
Problems you can solve with this pattern
5 problems · click any to start solving
// Maximum non-overlapping intervals (activity selection)
function maxNonOverlapping(intervals) {
intervals.sort((a, b) => a[1] - b[1]); // sort by end time
let count = 0, lastEnd = -Infinity;
for (const [start, end] of intervals) {
if (start >= lastEnd) { // no overlap with last selected
count++;
lastEnd = end;
}
}
return count;
}
// Minimum meeting rooms needed
function minMeetingRooms(intervals) {
const events = [];
for (const [s, e] of intervals) {
events.push([s, 1]); // start
events.push([e, -1]); // end (end first if tie, so use -1 in sort)
}
events.sort((a, b) => a[0] - b[0] || a[1] - b[1]); // end before start at same time
let rooms = 0, maxRooms = 0;
for (const [, type] of events) {
rooms += type;
maxRooms = Math.max(maxRooms, rooms);
}
return maxRooms;
}Activity selection greedy: sort by end time. Always pick the interval with the earliest end time that doesn't conflict with the last picked. This is optimal. Meeting rooms: sweep events (start: +1, end: -1), track running max of concurrent events. Interval covering: sort by start, greedily extend reach. Jump Game variant: how far can you reach from current position?
// Maximum non-overlapping intervals (activity selection)
function maxNonOverlapping(intervals) {
intervals.sort((a, b) => a[1] - b[1]); // sort by end time
let count = 0, lastEnd = -Infinity;
for (const [start, end] of intervals) {
if (start >= lastEnd) { // no overlap with last selected
count++;
lastEnd = end;
}
}
return count;
}
// Minimum meeting rooms needed
function minMeetingRooms(intervals) {
const events = [];
for (const [s, e] of intervals) {
events.push([s, 1]); // start
events.push([e, -1]); // end (end first if tie, so use -1 in sort)
}
events.sort((a, b) => a[0] - b[0] || a[1] - b[1]); // end before start at same time
let rooms = 0, maxRooms = 0;
for (const [, type] of events) {
rooms += type;
maxRooms = Math.max(maxRooms, rooms);
}
return maxRooms;
}- By end time: maximize non-overlapping (activity selection)
- By start time: covering problems (Jump Game, Video Stitching)
- Both sorted: two-pointer meeting rooms trick
Proof of optimality for activity selection: Exchange argument — if any optimal solution doesn't pick the earliest-ending interval, we can swap it in without reducing the count.
Common trap: Intervals that touch (end == start) — define as overlapping or not? Always clarify. For meeting rooms: [1,2] and [2,3] — if end is exclusive, no overlap; if inclusive, they share time 2.