Skip to content

Commit 340dd1f

Browse files
Maximum Number of Events That Can Be Attended - Leetcode 1353
1 parent 0ae2611 commit 340dd1f

File tree

4 files changed

+210
-0
lines changed

4 files changed

+210
-0
lines changed
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package priorityqueue.maximum_events_that_can_be_attended;
2+
3+
import java.util.Arrays;
4+
import java.util.Comparator;
5+
import java.util.PriorityQueue;
6+
import java.util.Queue;
7+
8+
/**
9+
* Solves the "Maximum Number of Events That Can Be Attended" problem using a greedy approach
10+
* with a min-priority queue.
11+
*
12+
* <p>The core idea is to iterate through each day and, on any given day, attend the available
13+
* event that will end the soonest. This greedy choice ensures that we leave future days open
14+
* for events that last longer.
15+
*
16+
* <p><b>Algorithm:</b>
17+
* <ol>
18+
* <li>Sort the events by their start day. This allows us to process events chronologically.</li>
19+
* <li>Initialize a min-priority queue to store the end days of events that are currently available to attend.</li>
20+
* <li>Iterate through the days from the first possible start day to the last possible end day.</li>
21+
* <li>On each day, add all events that start on that day to the priority queue. The queue will store their end days.</li>
22+
* <li>Remove any events from the queue that have already ended (i.e., their end day is before the current day).</li>
23+
* <li>If there are any events in the queue, attend one. We greedily pick the one that finishes earliest
24+
* (the top of the min-queue), increase our attended events count, and move to the next day.</li>
25+
* </ol>
26+
*
27+
* <p><b>Complexity Analysis:</b>
28+
* <ul>
29+
* <li>Time Complexity: O(N log N), dominated by the initial sort of the events. The while loop processes each event
30+
* and day once, and each priority queue operation is O(log K), where K is the number of active events.
31+
* <li>Space Complexity: O(N) in the worst case, where the priority queue could hold all events if they all start on the same day.
32+
* </ul>
33+
*
34+
* @see <a href="https://leetcode.com/problems/maximum-number-of-events-that-can-be-attended/">1353. Maximum Number of Events That Can Be Attended</a>
35+
*/
36+
public class PriorityQueueSolution {
37+
38+
/**
39+
* Calculates the maximum number of events that can be attended.
40+
*
41+
* @param events A 2D array where {@code events[i] = [startDay, endDay]}.
42+
* @return The maximum number of events that can be attended.
43+
*/
44+
public int maxEvents(int[][] events) {
45+
// Sort events by their start day to process them in order.
46+
Arrays.sort(events, Comparator.comparingInt(a -> a[0]));
47+
48+
// A min-priority queue to store the end days of events that are currently available.
49+
// The greedy choice is to always attend the event that finishes earliest.
50+
Queue<Integer> queue = new PriorityQueue<>();
51+
52+
int day = 0; // Current day
53+
int index = 0; // Pointer for the events array
54+
int result = 0; // Total number of events attended
55+
int numberOfEvents = events.length;
56+
57+
// The main loop continues as long as there are events to process or events in the queue to attend.
58+
while (index < numberOfEvents || !queue.isEmpty()) {
59+
// If the queue is empty, we can fast-forward time to the start day of the next available event.
60+
if (queue.isEmpty()) {
61+
day = events[index][0];
62+
}
63+
64+
// Add all events that start on the current day to the priority queue.
65+
while (index < numberOfEvents && events[index][0] == day) {
66+
queue.offer(events[index][1]);
67+
index++;
68+
}
69+
70+
// Attend the event that ends the soonest.
71+
queue.poll();
72+
result++;
73+
day++; // Move to the next day.
74+
75+
// Clean up the queue by removing events that have already ended.
76+
// An event with endDay < current day cannot be attended.
77+
while (!queue.isEmpty() && queue.peek() < day) {
78+
queue.poll();
79+
}
80+
}
81+
return result;
82+
}
83+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* This package contains a solution for the LeetCode problem:
3+
* "1353. Maximum Number of Events That Can Be Attended".
4+
*
5+
* <p>Problem Statement:
6+
* You are given an array of events where {@code events[i] = [startDayi, endDayi]}.
7+
* Every event {@code i} starts at {@code startDayi} and ends at {@code endDayi}.
8+
*
9+
* <p>You can attend an event {@code i} on any day {@code d} where
10+
* {@code startDayi <= d <= endDayi}. You can only attend one event at any given day {@code d}.
11+
*
12+
* <p>The goal is to return the maximum number of events you can attend.
13+
*
14+
* <p><b>Constraints:</b>
15+
* <ul>
16+
* <li>{@code 1 <= events.length <= 10^5}</li>
17+
* <li>{@code events[i].length == 2}</li>
18+
* <li>{@code 1 <= startDayi <= endDayi <= 10^5}</li>
19+
* </ul>
20+
*
21+
* @see <a href="https://leetcode.com/problems/maximum-number-of-events-that-can-be-attended/">
22+
* 1353. Maximum Number of Events That Can Be Attended</a>
23+
*/
24+
package priorityqueue.maximum_events_that_can_be_attended;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* This package contains implementations of the Priority Queue data structure.
3+
*
4+
* <p>A priority queue is an abstract data type that operates like a regular queue
5+
* but with a twist: each element has an associated "priority". Elements with
6+
* higher priority are served before elements with lower priority. If two elements
7+
* share the same priority, their order is determined by their position in the queue.
8+
*/
9+
package priorityqueue;
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package priorityqueue.maximum_events_that_can_be_attended;
2+
3+
import org.junit.jupiter.api.BeforeEach;
4+
import org.junit.jupiter.api.Test;
5+
6+
import static org.junit.jupiter.api.Assertions.assertEquals;
7+
8+
/**
9+
* Unit tests for the {@link PriorityQueueSolution} class.
10+
*/
11+
class PriorityQueueSolutionTest {
12+
13+
private PriorityQueueSolution solution;
14+
15+
@BeforeEach
16+
void setUp() {
17+
solution = new PriorityQueueSolution();
18+
}
19+
20+
@Test
21+
void testLeetCodeExample1() {
22+
int[][] events = {{1, 2}, {2, 3}, {3, 4}};
23+
assertEquals(3, solution.maxEvents(events), "Should be able to attend all three sequential events.");
24+
}
25+
26+
@Test
27+
void testLeetCodeExample2() {
28+
int[][] events = {{1, 2}, {2, 3}, {3, 4}, {1, 2}};
29+
assertEquals(4, solution.maxEvents(events), "Should attend all four events by careful scheduling.");
30+
}
31+
32+
@Test
33+
void testComplexOverlap() {
34+
int[][] events = {{1, 4}, {4, 4}, {2, 2}, {3, 4}, {1, 1}};
35+
// Day 1: Attend {1,1}
36+
// Day 2: Attend {2,2}
37+
// Day 3: Attend {1,4} (available since day 1)
38+
// Day 4: Attend {3,4} or {4,4}
39+
assertEquals(4, solution.maxEvents(events), "Should correctly handle complex overlapping events.");
40+
}
41+
42+
@Test
43+
void testNoOverlapEvents() {
44+
int[][] events = {{1, 2}, {4, 5}, {7, 8}};
45+
assertEquals(3, solution.maxEvents(events), "Should attend all events when there is no overlap.");
46+
}
47+
48+
@Test
49+
void testFullOverlapOnSingleDay() {
50+
int[][] events = {{1, 1}, {1, 1}, {1, 1}};
51+
assertEquals(1, solution.maxEvents(events), "Should only attend one event if all are on the same day.");
52+
}
53+
54+
@Test
55+
void testIdenticalLongEvents() {
56+
int[][] events = {{1, 5}, {1, 5}, {1, 5}};
57+
// Day 1: Attend one {1,5}
58+
// Day 2: Attend another {1,5}
59+
// Day 3: Attend the last {1,5}
60+
assertEquals(3, solution.maxEvents(events), "Should attend one of the identical events on each available day.");
61+
}
62+
63+
@Test
64+
void testEdgeCaseEmptyEvents() {
65+
int[][] events = {};
66+
assertEquals(0, solution.maxEvents(events), "Should return 0 for an empty array of events.");
67+
}
68+
69+
@Test
70+
void testEdgeCaseSingleEvent() {
71+
int[][] events = {{10, 12}};
72+
assertEquals(1, solution.maxEvents(events), "Should attend the single available event.");
73+
}
74+
75+
@Test
76+
void testEventsThatStartLate() {
77+
int[][] events = {{100, 100}, {100, 101}};
78+
// Day 100: Attend {100, 100}
79+
// Day 101: Attend {100, 101}
80+
assertEquals(2, solution.maxEvents(events), "Should handle events that start on a late day.");
81+
}
82+
83+
@Test
84+
void testGreedyChoiceIsOptimal() {
85+
// On day 2, events {1,2} and {2,3} are available.
86+
// The greedy choice is to attend {1,2} because it ends sooner.
87+
// Day 1: Attend {1,1}
88+
// Day 2: Attend {1,2} (ends today)
89+
// Day 3: Attend {2,3}
90+
// Day 4: Attend {3,4}
91+
int[][] events = {{1, 2}, {1, 1}, {2, 3}, {3, 4}};
92+
assertEquals(4, solution.maxEvents(events), "Should make the correct greedy choice to maximize events.");
93+
}
94+
}

0 commit comments

Comments
 (0)