Maximum Number of Events That Can Be Attended
MedPrompt
You are given an array of events where events[i] = [startDayi, endDayi]. Every event i starts at startDayi and ends at endDayi.
You can attend an event i at any day d where startTimei <= d <= endTimei. You can only attend one event at any time d.
Return the maximum number of events you can attend.
Example 1:
Input: events = [[1,2],[2,3],[3,4]]
Output: 3
Explanation: You can attend all the three events.
One way to attend them all is as shown.
Attend the first event on day 1.
Attend the second event on day 2.
Attend the third event on day 3.Example 2:
Input: events= [[1,2],[2,3],[3,4],[1,2]]
Output: 4
Constraints:
1 <= events.length <= 105events[i].length == 21 <= startDayi <= endDayi <= 105
Approaches
2 approaches with complexity analysis and trade-offs.
This approach uses a greedy strategy. The core idea is to prioritize events that finish earlier. By attending events that are about to expire, we leave more flexibility for events that last longer. We sort the events by their end day. Then, for each event, we try to attend it on the earliest possible day available within its time window.
Algorithm
-
- Sort the
eventsarray based on theendDayin ascending order.
- Sort the
-
- Initialize a set
attendedDaysto store the days on which an event has been scheduled.
- Initialize a set
-
- Initialize a counter
countto 0.
- Initialize a counter
-
- Iterate through each
eventin the sorted array:
- Iterate through each
-
- a. For the current
eventwith[startDay, endDay], iterate fromd = startDaytoendDay.
- a. For the current
-
- b. If day
dis not inattendedDays:
- b. If day
-
-
i. Add `d` to `attendedDays`.
-
-
-
ii. Increment `count`.
-
-
-
iii. Break the inner loop and proceed to the next event.
-
-
- Return
count.
- Return
Walkthrough
The algorithm first sorts all events in ascending order of their endDay. This is a crucial greedy choice: we always deal with the events that are closest to their deadline.
We then iterate through these sorted events. For each event [startDay, endDay], we try to find an available day to attend it.
To maximize our chances for future events, we should use the earliest possible day for the current event. Therefore, we scan from startDay to endDay.
We use a boolean array or a hash set, let's call it usedDays, to keep track of the days on which we've already scheduled an event.
For the current event, we look for the first day d in its range [startDay, endDay] such that usedDays[d] is false. If we find such a day, we 'attend' the event on day d by setting usedDays[d] to true, incrementing our count of attended events, and moving on to the next event.
If we iterate through the entire range [startDay, endDay] and find no available days, we cannot attend this event and simply move to the next one.
import java.util.Arrays;import java.util.HashSet; class Solution { public int maxEvents(int[][] events) { // Sort events by their end day. Arrays.sort(events, (a, b) -> a[1] - b[1]); HashSet<Integer> attendedDays = new HashSet<>(); int count = 0; for (int[] event : events) { int startDay = event[0]; int endDay = event[1]; // Find the earliest available day for this event. for (int d = startDay; d <= endDay; d++) { if (!attendedDays.contains(d)) { attendedDays.add(d); count++; break; // Move to the next event } } } return count; }}The provided code uses a HashSet to track attended days. A boolean array could also be used if the maximum day is known and within reasonable memory limits, which might offer slightly better performance for lookups.
Complexity
Time
O(N*D + N log N), where N is the number of events and D is the maximum range of days for an event. Sorting takes O(N log N). The nested loops can take up to O(N * D) in the worst case, for example, if all events are `[1, 100000]`. This leads to Time Limit Exceeded on larger test cases.
Space
O(D_max), where D_max is the maximum end day among all events. This is for the `attendedDays` set or boolean array. Given the constraints, this is acceptable.
Trade-offs
Pros
The logic is relatively straightforward to understand.
It correctly implements a valid greedy strategy.
Cons
The time complexity is too high due to the linear scan for an available day for each event, making it inefficient for the given constraints.
This is a more efficient greedy approach that processes the events day by day. Instead of iterating through events, we iterate through time (days). We use a min-heap to keep track of all events that have started but have not yet been attended. The greedy choice at each day is to attend the available event that finishes the soonest, as this leaves events that last longer available for future days.
Algorithm
-
- Sort the
eventsarray based on thestartDayin ascending order.
- Sort the
-
- Initialize a min-heap
pqto store the end days of active events.
- Initialize a min-heap
-
- Initialize
count = 0and an event pointeri = 0.
- Initialize
-
- Iterate through each day
dfrom 1 to the maximum possible day (e.g., 100000).
- Iterate through each day
-
- a. Remove all elements from the top of
pqthat are less thand(these events have expired).
- a. Remove all elements from the top of
-
- b. While the event pointer
iis within bounds andevents[i][0] == d, addevents[i][1]topqand incrementi.
- b. While the event pointer
-
- c. If
pqis not empty, it means we can attend an event today. Incrementcountand remove the top element frompq(attending the event that ends soonest).
- c. If
-
- Return
count.
- Return
Walkthrough
The key idea is to iterate through each day d from the first possible start day to the last possible end day.
First, we sort the events by their startDay. This allows us to efficiently find all events that become available on a given day d.
We use a min-heap (PriorityQueue in Java) to store the endDay of events that are currently available. An event is available if its startDay has passed, and we haven't attended it yet.
The algorithm proceeds as follows:
We iterate d from 1 up to the maximum possible day. An event pointer i tracks our position in the sorted events array.
On each day d:
- Add new events: We add the
endDayof all events that start on daydinto the min-heap. We advance the event pointeriaccordingly. - Remove expired events: We remove any
endDayfrom the top of the heap ifendDay < d. These represent events that we can no longer attend because their window has closed. - Attend an event: If the heap is not empty after the cleanup, it means there's at least one valid event we can attend today. We greedily choose the one that ends the soonest (which is at the top of the min-heap). We increment our attended events
countand remove that event'sendDayfrom the heap.
This process ensures that on any given day, we prioritize the most urgent event, which is a powerful greedy strategy that leads to the optimal solution.
import java.util.Arrays;import java.util.PriorityQueue; class Solution { public int maxEvents(int[][] events) { // Sort events by start day Arrays.sort(events, (a, b) -> a[0] - b[0]); // Min-heap to store end days of available events PriorityQueue<Integer> pq = new PriorityQueue<>(); int i = 0; // event pointer int count = 0; int n = events.length; // Iterate through all possible days for (int d = 1; d <= 100000; d++) { // Remove events from the heap that have already ended while (!pq.isEmpty() && pq.peek() < d) { pq.poll(); } // Add new events that start on the current day while (i < n && events[i][0] == d) { pq.offer(events[i][1]); i++; } // If there are available events, attend one if (!pq.isEmpty()) { pq.poll(); // Attend the one that ends soonest count++; } // Optimization: if all events are processed and heap is empty, we can stop if (i >= n && pq.isEmpty()) { break; } } return count; }}Complexity
Time
O(N log N + D), where N is the number of events and D is the range of days. Sorting takes O(N log N). The main loop runs D times. Each event is pushed and popped from the heap exactly once, contributing O(N log N) over all iterations. Thus, the total time complexity is dominated by sorting and the day loop, resulting in O(N log N + D).
Space
O(N) in the worst case. The min-heap can store up to N events if all events start on the same day.
Trade-offs
Pros
Highly efficient and passes the given constraints.
The greedy choice is optimal and guarantees the correct maximum number of events.
Cons
The logic is slightly more complex than the naive greedy approach, involving a heap and careful iteration over days.
Solutions
Solution
class Solution { public int maxEvents ( int [][] events ) { Map < Integer , List < Integer >> d = new HashMap <>(); int i = Integer . MAX_VALUE , j = 0 ; for ( var v : events ) { int s = v [ 0 ], e = v [ 1 ]; d . computeIfAbsent ( s , k -> new ArrayList <>()). add ( e ); i = Math . min ( i , s ); j = Math . max ( j , e ); } PriorityQueue < Integer > q = new PriorityQueue <>(); int ans = 0 ; for ( int s = i ; s <= j ; ++ s ) { while (! q . isEmpty () && q . peek () < s ) { q . poll (); } for ( int e : d . getOrDefault ( s , Collections . emptyList ())) { q . offer ( e ); } if (! q . isEmpty ()) { q . poll (); ++ ans ; } } return ans ; } }class Solution { public: int maxEvents ( vector < vector < int >>& events ) { unordered_map < int , vector < int >> d ; int i = INT_MAX , j = 0 ; for ( auto & v : events ) { int s = v [ 0 ], e = v [ 1 ]; d [ s ]. push_back ( e ); i = min ( i , s ); j = max ( j , e ); } priority_queue < int , vector < int > , greater < int >> q ; int ans = 0 ; for ( int s = i ; s <= j ; ++ s ) { while ( q . size () && q . top () < s ) { q . pop (); } for ( int e : d [ s ]) { q . push ( e ); } if ( q . size ()) { ++ ans ; q . pop (); } } return ans ; } };class Solution : def maxEvents ( self , events : List [ List [ int ]]) -> int : d = defaultdict ( list ) i , j = inf , 0 for s , e in events : d [ s ]. append ( e ) i = min ( i , s ) j = max ( j , e ) h = [] ans = 0 for s in range ( i , j + 1 ): while h and h [ 0 ] < s : heappop ( h ) for e in d [ s ]: heappush ( h , e ) if h : ans += 1 heappop ( h ) return ansVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.