Homeβ€Ί DSAβ€Ί Interval Scheduling and Meeting Rooms Problem

Interval Scheduling and Meeting Rooms Problem

Where developers are forged. Β· Structured learning Β· Free forever.
πŸ“ Part of: Greedy & Backtracking β†’ Topic 12 of 13
Solve interval scheduling maximisation and meeting rooms problems using greedy algorithms.
βš™οΈ Intermediate β€” basic DSA knowledge assumed
In this tutorial, you'll learn:
  • Max non-overlapping: sort by END time (not start time β€” this is the common mistake), pick greedily. Sorting by start time gives a suboptimal result.
  • Min meeting rooms: sort by start time, use a min-heap of end times. Size of heap at any point = rooms in use. Peak heap size = answer.
  • Merge intervals: sort by start time, linear scan β€” merge if next start <= current end. O(n log n) for sort, O(n) for merge.
✦ Plain-English analogy ✦ Real code with output ✦ Interview questions
⚑ Quick Answer
You have a list of meetings, each with a start and end time. Two problems: (1) What is the maximum number of non-overlapping meetings you can attend? (2) What is the minimum number of rooms needed to host all meetings simultaneously? Both have elegant greedy solutions.

Interval scheduling appears in every operating system (process time slots), every calendar application (meeting conflict detection), and virtually every resource allocation problem. The three variants β€” maximise non-overlapping intervals, minimise rooms needed, merge overlapping intervals β€” are among the most frequently asked algorithmic interview problems at top companies.

All three have elegant O(n log n) greedy solutions, but the correctness argument for each is different. Maximum non-overlapping uses earliest deadline first with an exchange argument. Minimum rooms uses a priority queue sweep. Merge intervals is just careful iteration. Knowing all three and why each works β€” not just what to code β€” is what distinguishes thorough preparation.

Problem 1 β€” Maximum Non-Overlapping Intervals

Greedy insight: always pick the interval that finishes earliest. This leaves maximum room for future intervals.

interval_scheduling.py Β· PYTHON
1234567891011121314151617
def max_non_overlapping(intervals: list[tuple]) -> list[tuple]:
    """
    Returns maximum set of non-overlapping intervals.
    Greedy: sort by end time, always pick earliest finish.
    """
    intervals = sorted(intervals, key=lambda x: x[1])
    selected = []
    last_end = float('-inf')
    for start, end in intervals:
        if start >= last_end:  # no overlap
            selected.append((start, end))
            last_end = end
    return selected

intervals = [(1,4),(3,5),(0,6),(5,7),(3,9),(5,9),(6,10),(8,11),(8,12),(2,14),(12,16)]
result = max_non_overlapping(intervals)
print(f'Max non-overlapping: {len(result)} β†’ {result}')
β–Ά Output
Max non-overlapping: 4 β†’ [(1, 4), (5, 7), (8, 11), (12, 16)]

Problem 2 β€” Minimum Meeting Rooms

Use a min-heap tracking end times of active meetings. For each meeting start, if the earliest-ending active meeting has ended, reuse its room.

meeting_rooms.py Β· PYTHON
12345678910111213141516
import heapq

def min_meeting_rooms(intervals: list[tuple]) -> int:
    if not intervals:
        return 0
    intervals = sorted(intervals, key=lambda x: x[0])
    heap = []  # end times of active meetings
    for start, end in intervals:
        if heap and heap[0] <= start:
            heapq.heapreplace(heap, end)  # reuse room
        else:
            heapq.heappush(heap, end)     # new room needed
    return len(heap)

print(min_meeting_rooms([(0,30),(5,10),(15,20)]))  # 2
print(min_meeting_rooms([(7,10),(2,4)]))             # 1
β–Ά Output
2
1

Problem 3 β€” Merge Overlapping Intervals

Sort by start time, merge consecutive overlapping intervals.

merge_intervals.py Β· PYTHON
1234567891011121314
def merge_intervals(intervals: list[tuple]) -> list[tuple]:
    if not intervals:
        return []
    intervals = sorted(intervals, key=lambda x: x[0])
    merged = [intervals[0]]
    for start, end in intervals[1:]:
        if start <= merged[-1][1]:           # overlapping
            merged[-1] = (merged[-1][0], max(merged[-1][1], end))
        else:
            merged.append((start, end))
    return merged

print(merge_intervals([(1,3),(2,6),(8,10),(15,18)]))  # [(1,6),(8,10),(15,18)]
print(merge_intervals([(1,4),(4,5)]))                  # [(1,5)]
β–Ά Output
[(1, 6), (8, 10), (15, 18)]
[(1, 5)]

Why Earliest Finish Time is Optimal

Claim: picking the interval with the earliest finish time is always in some optimal solution. Proof: suppose optimal starts with interval A instead of E (earliest finish). Replace A with E β€” since E finishes earlier, this can only improve future compatibility. By induction, greedy is optimal.

🎯 Key Takeaways

  • Max non-overlapping: sort by END time (not start time β€” this is the common mistake), pick greedily. Sorting by start time gives a suboptimal result.
  • Min meeting rooms: sort by start time, use a min-heap of end times. Size of heap at any point = rooms in use. Peak heap size = answer.
  • Merge intervals: sort by start time, linear scan β€” merge if next start <= current end. O(n log n) for sort, O(n) for merge.
  • Why earliest deadline works: if you could improve by not taking the earliest-finishing interval first, you could swap it in β€” but swapping never makes things worse. Exchange argument.
  • LeetCode connection: 435 (non-overlapping), 252/253 (meeting rooms), 56 (merge intervals). These four problems cover 80% of interval scheduling interview questions.

Interview Questions on This Topic

  • QWhy does sorting by end time give the optimal solution for interval scheduling?
  • QHow does the min-heap approach solve the minimum meeting rooms problem?
  • QWhat is the difference between interval scheduling maximisation and minimum room allocation?
  • QHow would you find the minimum number of intervals to remove to make the rest non-overlapping?

Frequently Asked Questions

Is sorting by start time or end time correct for max non-overlapping?

End time. Sorting by start time is a common mistake β€” it doesn't give optimal results. Example: [(0,100),(1,2),(3,4)] β€” earliest start is (0,100) which blocks everything; earliest finish (1,2) is much better.

πŸ”₯
Naren Founder & Author

Developer and founder of TheCodeForge. I built this site because I was tired of tutorials that explain what to type without explaining why it works. Every article here is written to make concepts actually click.

← PreviousHuffman Encoding β€” Greedy Data CompressionNext β†’Gas Station Problem β€” Greedy Circular Tour
Forged with πŸ”₯ at TheCodeForge.io β€” Where Developers Are Forged