Activity Selection
Sort the activities by finish time. Walk through them in that order and keep lastFinish. Take an activity if it starts strictly after lastFinish, then set lastFinish to its finish. Otherwise skip it.
An activity can be taken only if it starts strictly after lastFinish: start > lastFinish. Starting at exactly lastFinish is not allowed, because the rule is strict.
Problem
You have a list of activities. Activity i runs from time start[i] to time finish[i]. One person can do only one activity at a time.
Return the largest number of activities that one person can do.
The back-to-back rule is strict. If you finish an activity at time x, the next one must start after x. Starting at exactly x is not allowed. An activity can also be instant, with start = finish.
Two arrays start and finish of the same length n. Activity i is the pair [start[i], finish[i]].
One integer: the largest number of activities you can do.
Pick activities 0, 1, 3 and 4: the times (1, 2), (3, 4), (5, 7) and (8, 9). Each one starts after the one before it finishes.
Activity 0 finishes at 20 and activity 2 starts at 20. That is not strictly after, so they can't both be done.
- • 1 ≤ n ≤ 2 · 10^5, where n = start.length = finish.length
- • 0 ≤ start[i] ≤ finish[i] ≤ 10^9
First, think yourself
Before opening any solution, try to answer these on paper. Use hints only when stuck — each hint is counted.
- 1.
If I can only pick a few activities, which one should I pick first so that the most time is left for the rest?
- 2.
Two activities clash when their times overlap. How would I check that for just two of them?
- 3.
If a long activity comes first in the list, does taking it help me fit more activities?
- 4.
After each pick, which single number do I need to remember to know what can come next?
- 5.
If I sort the activities by one of their two times, which sort makes the next choice easy to check?
Brute force approach
Better approach
Optimized approach
Why does this algorithm work?
Dry run
All approaches compared
| Approach | Idea | Time | Space |
|---|---|---|---|
| Brute Force | Try all 2^n subsets; keep the largest one with no clashes | O(2^N · N²) | O(1) |
| Better | dp[i] = most activities that can end with activity i; check every earlier one | O(N²) | O(N) |
| Optimal | Sort by finish time; take an activity when it starts after the last one taken ends | O(N log N) | O(N) |
Why does this belong to this pattern?
- “largest number of activities”Pick as many as possible, so the goal is to keep the set small in time.
- “only one activity at a time”Picked activities must not overlap, so they form a chain along the timeline.
- “next must start after x”The check is strict: start > previous finish, not ≥.
This is the anchor of the interval-greedy sub-pattern. The key choice is the sort key: finish time, not start time. Non-Overlapping Intervals uses the same key, but it counts what it removes. Here we count what we keep. Its check is ≥ because touching is allowed there; this problem uses strict >, so a shared time point is a clash.
Don't make this mistake
This looks like Merge Intervals, but it takes whatever starts first, and a long activity that starts early blocks everything after it. On the dry-run input, start = [1, 3, 0, 5, 8, 5] and finish = [2, 4, 6, 7, 9, 9]: sorting by start puts (0,6) first. It is taken, lastFinish = 6, and then only (8,9) fits, so the answer is 2. Sorting by finish gives 4.
A short activity can sit in the middle of two good ones. Take L = (0,5), M = (3,6), R = (6,12). M is the shortest (length 3), so it is taken first. It clashes with L (3 > 5 is false) and with R (6 > 6 is false), so the answer is 1. Taking L and then R (6 > 5) gives 2.
The rule is strict. In example 2, activity 0 finishes at 20 and activity 2 starts at 20. With ≥ both are taken and the answer is 2 instead of 1.
Each activity is one start and one finish that belong together. Sorting the two arrays on their own sends the starts and finishes to the wrong activities. On the dry-run input the sorted starts are 0, 1, 3, 5, 5, 8, and no longer match the finishes they came with.
Want to implement this?
Write it yourself on GeeksforGeeks, then come back and compare your code with the solutions above.
Open coding workspaceHow this concept connects to System Design & C++ Systems
Top engineering interviews test whether you understand how algorithms like Greedy translate into production architectures and low-level memory mechanics.
Foundations: Object-Oriented Principles in C++23
Class invariants, pure virtual interfaces, and RAII smart pointers.
Food Delivery System (Zomato Architecture)
Real-world machine coding application of algorithms and design patterns.
Pointers vs References Deep Dive
Pointer arithmetic and memory layout under the hood.
Virtual Method Table (vtable) & Memory Layout
Polymorphism, vptr overhead, and multiple inheritance in memory.