· 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 than end_{i-1} and cannot create a new overlap with intervals[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, then O(n) scan.
  • Space: O(1) extra (in-place sort), or O(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 exceeds 1. 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
Back to Blog