2Brute2Best
🔥0

Activity Selection

Easy·GeeksforGeeksBasic Anchor
GeeksforGeeks
30-Second Mental ModelInterval scheduling — greedy by finish time
Complexity:O(2^N · N²)→O(N log N)
Key Insight

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.

Core Validity Invariant

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.

01

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.

Input

Two arrays start and finish of the same length n. Activity i is the pair [start[i], finish[i]].

Output

One integer: the largest number of activities you can do.

Example 1
Input: start = [1, 3, 0, 5, 8, 5], finish = [2, 4, 6, 7, 9, 9]
Output: 4

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.

Example 2
Input: start = [10, 12, 20], finish = [20, 25, 30]
Output: 1

Activity 0 finishes at 20 and activity 2 starts at 20. That is not strictly after, so they can't both be done.

Constraints
  • • 1 ≤ n ≤ 2 · 10^5, where n = start.length = finish.length
  • • 0 ≤ start[i] ≤ finish[i] ≤ 10^9
02

First, think yourself

Before opening any solution, try to answer these on paper. Use hints only when stuck — each hint is counted.

  1. 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. 2.

    Two activities clash when their times overlap. How would I check that for just two of them?

  3. 3.

    If a long activity comes first in the list, does taking it help me fit more activities?

  4. 4.

    After each pick, which single number do I need to remember to know what can come next?

  5. 5.

    If I sort the activities by one of their two times, which sort makes the next choice easy to check?

03

Brute force approach

04

Better approach

05

Optimized approach

06

Why does this algorithm work?

07

Dry run

08

All approaches compared

ApproachIdeaTimeSpace
Brute ForceTry all 2^n subsets; keep the largest one with no clashesO(2^N · N²)O(1)
Betterdp[i] = most activities that can end with activity i; check every earlier oneO(N²)O(N)
OptimalSort by finish time; take an activity when it starts after the last one taken endsO(N log N)O(N)
09

Why does this belong to this pattern?

Clues in the problem statement
  • “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 ≥.
Pick the most intervals+no two overlap+sort by finish time, take the earliest finisher→Interval scheduling — greedy by finish time

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.

10

Don't make this mistake

Pattern confusion: sorting by start time
✗ sort(acts by start); // then the same check: if (start > lastFinish) take
✓ sort by finish time, then the same check

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.

Sorting by the shortest duration
✗ sort by (finish − start), smallest first
✓ sort by finish time

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.

Using ≥ instead of > for the start check
✗ if (start >= lastFinish) { count++; lastFinish = finish; }
✓ if (start > lastFinish) { count++; lastFinish = finish; }

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.

Sorting start[] and finish[] separately
✗ sort(start.begin(), start.end()); sort(finish.begin(), finish.end());
✓ pair each activity as (finish, start), then sort the pairs

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.

11

Want to implement this?

Write it yourself on GeeksforGeeks, then come back and compare your code with the solutions above.

Open coding workspace
How did it go?
Beyond The LeetCode Problem

How this concept connects to System Design & C++ Systems

Bridge DSA with production engineering

Top engineering interviews test whether you understand how algorithms like Greedy translate into production architectures and low-level memory mechanics.