The Quest Begins (The "Why")
I still remember the first time I stared at a whiteboard covered in overlapping intervals during a mock interview. The interviewer asked, “What’s the maximum number of non‑overlapping meetings you can schedule?” My brain went into overdrive: try every combination? Backtrack? Dynamic programming? I felt like I was stuck in a hallway with a hundred doors, each leading to a different schedule, and I had no map.
Honestly, that panic is a rite of passage for anyone who’s ever faced a scheduling or resource‑allocation problem. The brute‑force approach explodes exponentially, and even a decent DP solution can feel overkill when you just need a quick, reliable answer. That’s when I stumbled onto the greedy idea: pick the meeting that finishes earliest, then repeat. It sounded almost too simple—like choosing the red pill because it looks shinier—but something in my gut told me it might actually work.
That moment was my “aha!” spark. If I could prove that always taking the earliest‑finishing interval never hurts the optimal solution, I’d have a lightning‑fast O(n) tool for a whole class of problems. Let’s see why that intuition holds up.
The Revelation (The Insight)
The Greedy Choice Property
Consider any set of intervals sorted by their finish times. Let I₁ be the interval that ends first. Suppose an optimal schedule OPT does not contain I₁. Because I₁ finishes earliest, the first interval in OPT (call it J) must finish no earlier than I₁ (otherwise J would have been chosen as the earliest finisher).
Now replace J with I₁ in OPT. Since I₁ ends no later than J, it cannot conflict with any interval that follows J in OPT (all those start after J finishes). Therefore the new schedule is still feasible and has the same number of intervals as OPT. We’ve just built another optimal schedule that does contain I₁.
Thus, there exists an optimal solution that picks the earliest‑finishing interval. After we lock in I₁, the problem reduces to the same type on the remaining intervals that start after I₁ ends. This recursive structure gives us the optimal sub‑structure property.
Proof Sketch in Plain English
- Pick the interval that finishes first.
- Throw away every interval that overlaps it (they can’t coexist).
- Repeat on what’s left.
Because each step never discards a chance to improve the total count (we proved an optimal solution can be transformed to include our choice), the greedy process builds an optimal schedule. No need to explore alternatives—just a single pass.
That’s the magic: a simple rule, a short exchange argument, and we get a proof that feels like discovering a hidden cheat code in a game.
Wielding the Power (Code & Examples)
Let’s turn the insight into code. I’ll use Python because it reads like pseudocode, but the logic translates directly to any language.
Classic Interview Problem #1: Maximum Number of Non‑Overlapping Meetings
Problem: Given a list of meetings with start and end times, return the maximum number of meetings you can attend without overlap.
This is exactly the Activity Selection problem.
def max_meetings(intervals):
"""
intervals: List[Tuple[int, int]] where each tuple is (start, end)
Returns: int – maximum count of non‑overlapping meetings
"""
# 1️⃣ Sort by finishing time (the greedy key)
intervals.sort(key=lambda x: x[1])
count = 0
last_end = float('-inf') # end time of the last chosen meeting
for start, end in intervals:
if start >= last_end: # no conflict with the previously chosen meeting
count += 1
last_end = end # lock in this meeting
return count
Why it’s O(n log n) → O(n) after sorting:
The dominant cost is the sort (O(n log n)). The scan itself is linear (O(n)). If the input is already sorted by end time (a common interview twist), the algorithm drops to pure O(n).
Common Pitfall (The Trap)
Many candidates sort by start time instead of end time. Imagine picking the meeting that starts earliest— you might grab a long meeting that blocks many short ones that could have fit. The greedy proof only works with the earliest finishing criterion, so always double‑check your sort key.
Classic Interview Problem #2: Minimum Platforms Needed at a Train Station
Problem: Given arrival and departure times of trains, find the minimum number of platforms required so that no train waits.
This is the “dual” of activity selection: we want the maximum overlap (the peak number of simultaneous intervals). A greedy sweep line works beautifully.
def min_platforms(arrivals, departures):
"""
arrivals, departures: List[int] of equal length
Returns: int – minimum platforms needed
"""
# 1️⃣ Sort both lists
arrivals.sort()
departures.sort()
platforms_needed = max_platforms = 0
i = j = 0
n = len(arrivals)
# 2️⃣ Merge‑like sweep
while i < n and j < n:
if arrivals[i] <= departures[j]:
# a train has arrived before the earliest departure -> need a platform
platforms_needed += 1
i += 1
max_platforms = max(max_platforms, platforms_needed)
else:
# a train has freed a platform
platforms_needed -= 1
j += 1
return max_platforms
Why it works:
We walk through time in chronological order. Every arrival increments the needed platforms; every departure decrements it. The peak value observed during this walk is exactly the minimum platforms required—because at any moment we cannot use fewer platforms than the number of trains simultaneously present.
Common Pitfall (The Trap)
Forgetting to sort both arrays leads to a wrong sweep. Also, using <= vs < for the arrival‑departure comparison matters when a train arrives exactly when another departs; most definitions allow the same platform, so we treat arrival <= departure as needing a new platform (conservative) or arrival < departure as reusing. Clarify with the interviewer—consistency is key.
Why This New Power Matters
Now you have a lightning‑fast, intuition‑driven tool for a whole family of scheduling, resource‑allocation, and selection problems:
- Interview readiness: You can tackle “maximum events”, “minimum meeting rooms”, “assign cookies”, “job sequencing with deadlines”, and more with confidence.
- Real‑world apps: Think of CPU task scheduling, conference room booking, ad slot allocation, or even optimizing delivery routes where you pick the earliest‑finishing delivery window.
- Performance edge: O(n log n) (or O(n) if pre‑sorted) beats exponential backtracking or O(n²) DP hand‑waving for large inputs.
The best part? You don’t need to memorize a slew of formulas. You just remember: sort by the natural “end” metric, greedily take what fits, and repeat. It’s like learning the Force in Star Wars—once you feel it, you can sense the right move without overthinking.
Your Next Quest
Here’s a challenge to solidify the power:
LeetCode 455 – Assign Cookies
Given children’s greed factors and cookie sizes, assign each child at most one cookie such that the number of content children is maximized.
Try solving it with the same greedy mindset: sort both arrays, then walk through them, giving the smallest sufficient cookie to each child.
Give it a go, drop your solution in the comments, and let’s celebrate when you feel that rush of “I just used the Force and it worked!”
Happy coding, and may your intervals always finish early! 🚀
Top comments (0)