· 6 min read
LeetCode Scheduling Patterns: Hotel/Intervals (Easy & Medium)
All the hotel/scheduling problems in one place: detect overlaps, min rooms, merge/insert intervals, non-overlap, arrows, car pooling. Clean JS templates.
Use these patterns to map hotel/scheduling questions to the right solution fast.
Quick Map
- Can attend all meetings? → sort by start, check overlaps
- Min number of rooms? → sort starts/ends; two pointers to count concurrency
- Merge overlapping intervals → sort by start, merge
- Insert interval → place, merge while overlapping
- Min removals to avoid overlap / max non-overlapping → greedy by end time
- Min arrows to burst balloons → greedy by end time
- Car Pooling feasibility → sweep events (pickup +cap, dropoff -cap)
Can Attend All Meetings (No Overlap) — Easy
Problem. Given an array intervals
where intervals[i] = [start_i, end_i]
(typically half-open [start, end)
), return true
if a person can attend all meetings (no overlaps), otherwise false
.
Input/Output.
- Input:
intervals: number[][]
- Output:
boolean
Constraints. 0 ≤ intervals.length ≤ 1e5
, -1e9 ≤ start_i < end_i ≤ 1e9
.
Example. [[0,30],[5,10],[15,20]] → false
, [[7,10],[2,4]] → true
.
function canAttendMeetings(intervals) {
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;
}
return true;
}
Why this works
- After sorting by start time, any overlap must appear between a meeting and its immediate predecessor. If
start_i < end_{i-1}
, there is a conflict; otherwise all earlier intervals end no later thanend_{i-1}
and cannot create a new overlap withintervals[i]
.
Interval semantics (critical)
- Prefer half-open intervals
[start, end)
unless the problem explicitly says inclusive end. - With
[start, end)
: back-to-back like[10, 20)
and[20, 30)
are allowed (20 < 20
is false). - If ends are inclusive, change the condition to
intervals[i][0] <= intervals[i - 1][1]
.
Edge cases
- Empty list or single meeting →
true
. - Negative times or large coordinates → sorting still works.
- Unsorted input → we sort first; do not assume input order.
Complexity
- Time:
O(n log n)
for sorting, thenO(n)
scan. - Space:
O(1)
extra (in-place sort), orO(n)
if you must keep original order.
Alternative (event sweep)
- Build events
(time, delta)
with(start, +1)
and(end, -1)
, sort by time then delta, and ensure the running count never exceeds1
. Overkill here but generalizes to multi-room.
Quick tests
console.log(
canAttendMeetings([
[0, 30],
[5, 10],
[15, 20],
])
); // false
console.log(
canAttendMeetings([
[7, 10],
[2, 4],
])
); // true
console.log(
canAttendMeetings([
[1, 2],
[2, 3],
[3, 4],
])
); // true (half-open)
console.log(canAttendMeetings([])); // true
Minimum Number of Meeting Rooms (Hotel Rooms) — Medium
Problem. Given intervals
of meeting times, return the minimum number of rooms required so that all meetings can be held without conflicts.
Input/Output.
- Input:
intervals: number[][]
- Output:
number
(minimum rooms)
Constraints. 1 ≤ intervals.length ≤ 1e5
, -1e9 ≤ start < end ≤ 1e9
.
Example. [[0,30],[5,10],[15,20]] → 2
, [[7,10],[2,4]] → 1
.
function minMeetingRooms(intervals) {
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 i = 0,
j = 0,
used = 0,
best = 0;
while (i < starts.length) {
if (starts[i] < ends[j]) {
// need a room
used++;
if (used > best) best = used;
i++;
} else {
// free a room
used--;
j++;
}
}
return best;
}
Merge Intervals — Medium
Problem. Given an array of intervals, merge all overlapping intervals and return an array of the non-overlapping intervals that cover all the intervals in the input.
Input/Output.
- Input:
intervals: number[][]
- Output:
number[][]
(merged)
Constraints. 1 ≤ intervals.length ≤ 1e5
, -1e9 ≤ start < end ≤ 1e9
.
Example. [[1,3],[2,6],[8,10],[15,18]] → [[1,6],[8,10],[15,18]]
.
function mergeIntervals(intervals) {
if (intervals.length <= 1) return intervals;
intervals.sort((a, b) => a[0] - b[0]);
const res = [intervals[0].slice()];
for (let i = 1; i < intervals.length; i++) {
const [s, e] = intervals[i];
const last = res[res.length - 1];
if (s <= last[1]) last[1] = Math.max(last[1], e);
else res.push([s, e]);
}
return res;
}
Insert Interval — Medium
Problem. You are given an array of non-overlapping intervals sorted by start time and a new interval newInterval
. Insert newInterval
into intervals
, merging if necessary, and return the resulting array.
Input/Output.
- Input:
intervals: number[][]
(sorted, non-overlapping),newInterval: [number, number]
- Output:
number[][]
Constraints. 0 ≤ intervals.length ≤ 1e5
, -1e9 ≤ start < end ≤ 1e9
.
Example. intervals = [[1,3],[6,9]], new = [2,5] → [[1,5],[6,9]]
.
function insertInterval(intervals, newInterval) {
const res = [];
let [ns, ne] = newInterval;
let i = 0;
while (i < intervals.length && intervals[i][1] < ns) res.push(intervals[i++]);
while (i < intervals.length && intervals[i][0] <= ne) {
ns = Math.min(ns, intervals[i][0]);
ne = Math.max(ne, intervals[i][1]);
i++;
}
res.push([ns, ne]);
while (i < intervals.length) res.push(intervals[i++]);
return res;
}
Non-overlapping Intervals (Min Removals) — Medium
Problem. Given a collection of intervals, find the minimum number of intervals you must remove to make the rest non-overlapping.
Input/Output.
- Input:
intervals: number[][]
- Output:
number
(min removals)
Constraints. 1 ≤ intervals.length ≤ 1e5
, -1e9 ≤ start < end ≤ 1e9
.
Example. [[1,2],[2,3],[3,4],[1,3]] → 1
(remove [1,3]
).
function eraseOverlapIntervals(intervals) {
if (intervals.length <= 1) return 0;
intervals.sort((a, b) => a[1] - b[1]);
let kept = 1;
let end = intervals[0][1];
for (let i = 1; i < intervals.length; i++) {
if (intervals[i][0] >= end) {
kept++;
end = intervals[i][1];
}
}
return intervals.length - kept;
}
Minimum Number of Arrows to Burst Balloons — Medium
Problem. You are given points
where points[i] = [x_start, x_end]
represents the horizontal diameter of a balloon. One arrow shot vertically at x
bursts all balloons with x_start ≤ x ≤ x_end
. Return the minimum number of arrows to burst all balloons.
Input/Output.
- Input:
points: number[][]
- Output:
number
Constraints. 0 ≤ points.length ≤ 1e5
, -1e9 ≤ x_start ≤ x_end ≤ 1e9
.
Example. [[10,16],[2,8],[1,6],[7,12]] → 2
.
function findMinArrowShots(points) {
if (points.length === 0) return 0;
points.sort((a, b) => a[1] - b[1]);
let arrows = 1;
let end = points[0][1];
for (let i = 1; i < points.length; i++) {
if (points[i][0] > end) {
// no overlap; need new arrow
arrows++;
end = points[i][1];
}
}
return arrows;
}
Car Pooling — Medium
Problem. You are given trips[i] = [numPassengers, from, to]
, where from
and to
are locations on a number line. Return true
if it is possible to pick up and drop off all passengers for all the given trips using a vehicle with capacity capacity
.
Input/Output.
- Input:
trips: number[][]
,capacity: number
- Output:
boolean
Constraints. 1 ≤ trips.length ≤ 1e5
, 0 ≤ numPassengers ≤ capacity ≤ 1e9
, 0 ≤ from < to ≤ 1e9
.
Example. trips = [[2,1,5],[3,3,7]], capacity = 4 → false
.
function carPooling(trips, capacity) {
const events = [];
for (const [passengers, start, end] of trips) {
events.push([start, passengers]);
events.push([end, -passengers]);
}
events.sort((a, b) => (a[0] === b[0] ? a[1] - b[1] : a[0] - b[0]));
let cur = 0;
for (const [, delta] of events) {
cur += delta;
if (cur > capacity) return false;
}
return true;
}
Pitfalls
- Use half-open intervals
[start, end)
when possible; consistent comparison - Sort by end-time for greedy selection; by start-time for merging
- Equal-time events: process drops before picks to free capacity
- Watch off-by-one in inclusive vs exclusive ends