Activity Selection finds the maximum number of non-overlapping intervals.
Sort by END time and sweep — see why the earliest-ending choice always wins:
⚠️ Animation & Content Notice
The animation work is not fully finished — some animations may have slight errors.
If there is a major error in the content or if the animation or content is difficult to understand, please contact us at rayyancodingschool@gmail.com.
Activity Selection
Pick the maximum number of non-overlapping activities.
Sort by END time, then sweep taking every activity that starts at or after the last chosen end. Ending early leaves the most room for what follows; sorting by start or duration fails. The greedy choice is always safe — one pass gives the optimal count.
1
sort activities by END time
2
take first activity; lastEnd = its end
3
for each next activity (s, e):
4
if s >= lastEnd:
5
take it; lastEnd = e
The key idea is simple:
Always choose the interval that finishes earliest.
Why?
An interval that finishes early leaves more time for the remaining intervals.
Focus on recognizing:
“Maximum number of non-overlapping intervals” = Sort by end time + Greedy
Pattern Table
| Pattern | Typical Questions | Trigger |
|---|---|---|
| Activity Selection | Maximum meetings/activities | Sort by end time |
| Minimum Meeting Rooms | Minimum rooms required | Count overlapping intervals |
Mental Trigger
Sort by end → Pick earliest finish → Skip overlaps → Repeat
1. Generic Activity Selection Template (Base)
This is the main template to remember.
public int activitySelection(int[][] intervals) {
if (intervals.length == 0) {
return 0;
}
Arrays.sort(intervals, (a, b) -> Integer.compare(a[1], b[1]));
int count = 1;
int lastEnd = intervals[0][1];
for (int i = 1; i < intervals.length; i++) {
int start = intervals[i][0];
int end = intervals[i][1];
if (start >= lastEnd) {
count++;
lastEnd = end;
}
}
return count;
}def activity_selection(intervals):
if not intervals:
return 0
intervals.sort(key=lambda x: x[1])
count = 1
last_end = intervals[0][1]
for i in range(1, len(intervals)):
start, end = intervals[i]
if start >= last_end:
count += 1
last_end = end
return countint activitySelection(vector<vector<int>>& intervals) {
if (intervals.empty()) {
return 0;
}
sort(intervals.begin(), intervals.end(),
[](const vector<int>& a, const vector<int>& b) {
return a[1] < b[1];
});
int count = 1;
int lastEnd = intervals[0][1];
for (int i = 1; i < (int)intervals.size(); i++) {
int start = intervals[i][0];
int end = intervals[i][1];
if (start >= lastEnd) {
count++;
lastEnd = end;
}
}
return count;
}function activitySelection(intervals) {
if (intervals.length === 0) {
return 0;
}
intervals.sort((a, b) => a[1] - b[1]);
let count = 1;
let lastEnd = intervals[0][1];
for (let i = 1; i < intervals.length; i++) {
const [start, end] = intervals[i];
if (start >= lastEnd) {
count++;
lastEnd = end;
}
}
return count;
}How it works
-
Sort intervals by their end time.
-
Pick the first interval.
-
For every next interval:
- If it starts after the previous selected interval ends, pick it.
- Otherwise, skip it.
Example
[1, 3]
[2, 4]
[3, 5]
[5, 7]
Sort by end:
[1, 3]
[2, 4]
[3, 5]
[5, 7]
Pick:
[1, 3]
[3, 5]
[5, 7]
Answer:
3
Pattern 1: Maximum Non-Overlapping Intervals
This is the core Activity Selection pattern.
What Changed from the Base Template?
Nothing.
This is the base problem.
The important part is recognizing:
Arrays.sort(intervals, (a, b) -> Integer.compare(a[1], b[1]));
and then:
if (intervals[i][0] >= lastEnd)
Activity Selection = Sort by end time + Greedily pick compatible intervals.
Pattern 2: Minimum Meeting Rooms
Problem Type
Instead of asking:
“How many meetings can I attend?”
the problem asks:
“How many rooms do I need so that all meetings can happen?”
For example:
[0, 30]
[5, 10]
[15, 20]
Two meetings can overlap, so we need:
2 rooms
Java Code
public int minMeetingRooms(int[][] intervals) {
if (intervals.length == 0) {
return 0;
}
int n = intervals.length;
int[] start = new int[n];
int[] end = new int[n];
for (int i = 0; i < n; i++) {
start[i] = intervals[i][0];
end[i] = intervals[i][1];
}
Arrays.sort(start);
Arrays.sort(end);
int rooms = 0;
int endIndex = 0;
for (int startTime : start) {
if (startTime < end[endIndex]) {
rooms++;
} else {
endIndex++;
}
}
return rooms;
}def min_meeting_rooms(intervals):
if not intervals:
return 0
n = len(intervals)
start = sorted(iv[0] for iv in intervals)
end = sorted(iv[1] for iv in intervals)
rooms = 0
end_index = 0
for start_time in start:
if start_time < end[end_index]:
rooms += 1
else:
end_index += 1
return roomsint minMeetingRooms(vector<vector<int>>& intervals) {
if (intervals.empty()) {
return 0;
}
int n = intervals.size();
vector<int> start(n), end(n);
for (int i = 0; i < n; i++) {
start[i] = intervals[i][0];
end[i] = intervals[i][1];
}
sort(start.begin(), start.end());
sort(end.begin(), end.end());
int rooms = 0;
int endIndex = 0;
for (int startTime : start) {
if (startTime < end[endIndex]) {
rooms++;
} else {
endIndex++;
}
}
return rooms;
}function minMeetingRooms(intervals) {
if (intervals.length === 0) {
return 0;
}
const n = intervals.length;
const start = intervals.map(iv => iv[0]).sort((a, b) => a - b);
const end = intervals.map(iv => iv[1]).sort((a, b) => a - b);
let rooms = 0;
let endIndex = 0;
for (const startTime of start) {
if (startTime < end[endIndex]) {
rooms++;
} else {
endIndex++;
}
}
return rooms;
}What Changed from the Base Template?
1. We no longer sort intervals by end time
Base:
Arrays.sort(intervals,
(a, b) -> Integer.compare(a[1], b[1]));
Changed to:
Arrays.sort(start);
Arrays.sort(end);
because we need to track when meetings start and end.
2. We track two timelines
Added:
int[] start;
int[] end;
because we need to know:
“Does the next meeting start before the earliest current meeting ends?“
3. Count overlapping meetings
Added:
if (startTime < end[endIndex]) {
rooms++;
}
If a meeting starts before another meeting ends:
start < end
we need another room.
Otherwise:
endIndex++;
a room becomes available.
Minimum Rooms = Sort starts + Sort ends + Two pointers.
Activity Selection Pattern Evolution
Activity Selection
↓
Sort by end time
↓
Pick earliest finishing interval
↓
Skip overlapping intervals
↓
Maximum number of activities
Minimum Meeting Rooms
↓
Sort start times + end times
↓
Sweep through both arrays
↓
Count simultaneous meetings
↓
Minimum rooms
Common Mistakes
1. Sorting by Start Time
Wrong:
Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
For Activity Selection, sort by end time.
Correct:
Arrays.sort(intervals,
(a, b) -> Integer.compare(a[1], b[1]));
2. Using > Instead of >=
If this is allowed:
[1, 3]
[3, 5]
then the activities do not overlap.
So use:
start >= lastEnd
If touching intervals are considered overlapping, use:
start > lastEnd
3. Forgetting the Empty Input Case
Wrong:
int lastEnd = intervals[0][1];
This fails when:
intervals.length == 0
Handle it first:
if (intervals.length == 0) {
return 0;
}
4. Confusing Activity Selection with Meeting Rooms
Maximum activities:
How many non-overlapping intervals can I select?
Think:
Sort by end → Greedy
Minimum rooms:
How many intervals can overlap at the same time?
Think:
Sort starts + ends → Two pointers
Recognition Cheat Sheet
| If you see… | Think… |
|---|---|
| Maximum non-overlapping intervals | Sort by end + Greedy |
| Maximum meetings you can attend | Activity Selection |
| Schedule maximum activities | Activity Selection |
| Minimum meeting rooms | Start/end sweep |
| Minimum resources | Sweep line |
| Maximum simultaneous intervals | Sweep line |
| Intervals overlap | Start/end events |
Premium Content
Unlock Activity Selection and all premium lessons with a subscription.
From ₹199.99/year — See plans