Jump into concepts and practice - no test required
or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
▶
Steps
setup
Collect start and end events
We iterate over each employee's schedule and extract start and end times as events, marking starts with +1 and ends with -1.
💡 Separating intervals into start and end events allows us to process all interval boundaries uniformly.
Line:for emp in schedule:
for interval in emp:
events.append((interval[0], 1)) # start event
events.append((interval[1], -1)) # end event
💡 This step transforms nested intervals into a flat list of events, preparing for sorting and sweeping.
setup
Sort events by time and type
We sort the events by their time; if two events have the same time, end events (-1) come before start events (+1).
💡 Sorting ensures we process events in chronological order and handle interval ends before starts at the same time.
Line:events.sort(key=lambda x: (x[0], x[1]))
💡 This ordering prevents counting intervals as active before they actually start when times tie.
setup
Initialize sweep line variables
We initialize 'active' to 0 to track how many intervals are currently active, and 'prev' to None to store the previous event time.
💡 Tracking active intervals helps us know when all employees are free (active == 0).
Line:free_times = []
active = 0
prev = None
💡 These variables set the stage for the sweep line to detect free time gaps.
traverse
Process first event (start at 1)
We process the first event at time 1, which is a start event (+1). Since active is 0 and prev is None, no free time is recorded. We increment active to 1 and set prev to 1.
💡 The first event starts an interval, so no free time can exist before it.
Line:for time, e_type in events:
if active == 0 and prev is not None and prev < time:
free_times.append([prev, time])
active += e_type
prev = time
💡 Active intervals increase, indicating employees are busy starting now.
traverse
Process second event (start at 1)
We process the second event, also a start at time 1. Active is currently 1, so no free time is recorded. We increment active to 2 and update prev to 1.
💡 Multiple intervals can start at the same time, increasing active count.
Line:active += e_type
prev = time
💡 Active count reflects overlapping intervals starting simultaneously.
traverse
Process third event (end at 2)
We process an end event at time 2. Active is 2, so no free time is recorded. We decrement active to 1 and update prev to 2.
💡 An interval ends, reducing the number of active intervals.
Line:active += e_type
prev = time
💡 Active count decreases as intervals close, tracking ongoing coverage.
traverse
Process fourth event (end at 3)
We process an end event at time 3. Active is 1, so no free time is recorded. We decrement active to 0 and update prev to 3.
💡 Active reaching zero means no intervals are currently covering the timeline.
Line:active += e_type
prev = time
💡 Active zero signals a potential start of free time after this event.
traverse
Process fifth event (start at 4)
We process a start event at time 4. Since active is 0 and prev is 3, and 3 < 4, we record free time [3,4]. Then we increment active to 1 and update prev to 4.
💡 When active is zero, the gap between prev and current time is free time for all employees.
Line:if active == 0 and prev is not None and prev < time:
free_times.append([prev, time])
active += e_type
prev = time
💡 This step reveals how the algorithm detects free time intervals between busy periods.
traverse
Process sixth event (start at 5)
We process a start event at time 5. Active is 1, so no free time is recorded. We increment active to 2 and update prev to 5.
💡 Intervals starting again increase active count, ending free time periods.
Line:active += e_type
prev = time
💡 Active count rising means employees are busy again.
traverse
Process seventh event (end at 6)
We process an end event at time 6. Active is 2, so no free time is recorded. We decrement active to 1 and update prev to 6.
💡 Intervals ending reduce active count but still indicate busy time.
Line:active += e_type
prev = time
💡 Active count tracks ongoing coverage accurately.
traverse
Process eighth event (end at 10)
We process the last event, an end at time 10. Active is 1, so no free time is recorded. We decrement active to 0 and update prev to 10.
💡 Active reaching zero at the end means all intervals have closed.
Line:active += e_type
prev = time
💡 The timeline is fully processed with no further free time after last interval.
reconstruct
Return the free time intervals
After processing all events, we return the list of free time intervals found, which is [[3,4]].
💡 The final answer is the list of gaps where no employee is working.
Line:return free_times
💡 The algorithm successfully identifies the free time between merged intervals.
from typing import List
def employeeFreeTime(schedule: List[List[List[int]]]) -> List[List[int]]:
events = [] # STEP 1
for emp in schedule: # STEP 1
for interval in emp: # STEP 1
events.append((interval[0], 1)) # start event # STEP 1
events.append((interval[1], -1)) # end event # STEP 1
events.sort(key=lambda x: (x[0], x[1])) # STEP 2
free_times = [] # STEP 3
active = 0 # STEP 3
prev = None # STEP 3
for time, e_type in events: # STEP 4 to 11
if active == 0 and prev is not None and prev < time: # STEP 8
free_times.append([prev, time]) # STEP 8
active += e_type # STEP 4 to 11
prev = time # STEP 4 to 11
return free_times # STEP 12
# Driver code
if __name__ == '__main__':
schedule = [[[1,2],[5,6]],[[1,3]],[[4,10]]]
print(employeeFreeTime(schedule))
📊
Employee Free Time - Watch the Algorithm Execute, Step by Step
Watching each event processed and how the active interval count changes reveals exactly when free time intervals appear, making the logic crystal clear.
Step 1/12
·Active fill★Answer cell
record
(1,1)
0
(2,-1)
1
(5,1)
2
(6,-1)
3
(1,1)
4
(3,-1)
5
(4,1)
6
(10,-1)
7
Result: []
record
(1,1)
0
(1,1)
1
(2,-1)
2
(3,-1)
3
(4,1)
4
(5,1)
5
(6,-1)
6
(10,-1)
7
Result: []
record
(1,1)
0
(1,1)
1
(2,-1)
2
(3,-1)
3
(4,1)
4
(5,1)
5
(6,-1)
6
(10,-1)
7
Result: []
move_right
i
(1,1)
0
(1,1)
1
(2,-1)
2
(3,-1)
3
(4,1)
4
(5,1)
5
(6,-1)
6
(10,-1)
7
Result: []
move_right
(1,1)
0
i
(1,1)
1
(2,-1)
2
(3,-1)
3
(4,1)
4
(5,1)
5
(6,-1)
6
(10,-1)
7
Result: []
move_right
(1,1)
0
(1,1)
1
i
(2,-1)
2
(3,-1)
3
(4,1)
4
(5,1)
5
(6,-1)
6
(10,-1)
7
Result: []
move_right
(1,1)
0
(1,1)
1
(2,-1)
2
i
(3,-1)
3
(4,1)
4
(5,1)
5
(6,-1)
6
(10,-1)
7
Result: []
record
(1,1)
0
(1,1)
1
(2,-1)
2
(3,-1)
3
i
(4,1)
4
(5,1)
5
(6,-1)
6
(10,-1)
7
Result: [[3,4]]
move_right
(1,1)
0
(1,1)
1
(2,-1)
2
(3,-1)
3
(4,1)
4
i
(5,1)
5
(6,-1)
6
(10,-1)
7
Result: [[3,4]]
move_right
(1,1)
0
(1,1)
1
(2,-1)
2
(3,-1)
3
(4,1)
4
(5,1)
5
i
(6,-1)
6
(10,-1)
7
Result: [[3,4]]
move_right
(1,1)
0
(1,1)
1
(2,-1)
2
(3,-1)
3
(4,1)
4
(5,1)
5
(6,-1)
6
i
(10,-1)
7
Result: [[3,4]]
record
(1,1)
0
(1,1)
1
(2,-1)
2
(3,-1)
3
(4,1)
4
(5,1)
5
(6,-1)
6
(10,-1)
7
Result: [[3,4]]
Key Takeaways
✓ Transforming intervals into start and end events allows a uniform sweep line approach.
This insight is hard to see from code alone because the flattening and sorting steps are subtle but crucial.
✓ Sorting events with end events before start events at the same time prevents counting intervals as active prematurely.
This ordering detail is easy to overlook but essential for correct active interval tracking.
✓ When the active count drops to zero, the gap between previous and current event times is free time.
Watching the active count change step-by-step reveals exactly when free time intervals appear.
Practice
(1/5)
1. Consider the following Python code implementing the optimal car pooling solution. What is the return value when called with trips = [[2,1,5],[3,3,7]] and capacity = 4?
def carPooling(trips, capacity):
max_location = 0
for _, start, end in trips:
max_location = max(max_location, end)
diff = [0] * (max_location + 1)
for num, start, end in trips:
diff[start] += num
diff[end] -= num
current_passengers = 0
for i in range(max_location + 1):
current_passengers += diff[i]
if current_passengers > capacity:
return false
return true
print(carPooling([[2,1,5],[3,3,7]], 4))
easy
A. Error due to index out of range
B. true
C. false
D. true only if capacity is at least 5
Solution
Step 1: Build difference array
diff after processing trips: diff[1]+=2, diff[5]-=2, diff[3]+=3, diff[7]-=3
Step 2: Sweep through diff to track passengers
At location 1: current_passengers=2; at 3: current_passengers=5 (2+3), which exceeds capacity=4 -> return false
Final Answer:
Option C -> Option C
Quick Check:
Passenger count exceeds capacity at location 3 [OK]
Hint: Sum diffs to track passengers, check capacity [OK]
Common Mistakes:
Off-by-one in diff array indexing
Checking capacity only at trip start
Forgetting to decrement at trip end
2. Given a list of train arrival and departure times, you need to find the minimum number of platforms required so that no train waits. Which approach guarantees an optimal solution for this problem?
easy
A. Use a greedy algorithm that assigns platforms as trains arrive without sorting times.
B. Use a sweep line algorithm by creating events for arrivals and departures, sorting them, and tracking concurrent trains.
C. Use dynamic programming to find the maximum overlap of intervals by building a state table.
D. Use nested loops to check every pair of trains for overlap and count maximum overlaps.
Solution
Step 1: Understand the problem requires tracking concurrent trains
The key is to find the maximum number of trains present at the station simultaneously.
Step 2: Recognize that sorting events and sweeping through them tracks concurrency efficiently
By creating arrival and departure events and sorting them, we can increment or decrement the count of trains present, capturing the peak count.
Final Answer:
Option B -> Option B
Quick Check:
Sweep line tracks concurrency optimally [OK]
Hint: Sort events and sweep to track concurrency [OK]
Common Mistakes:
Assuming greedy assignment without sorting works
Trying DP which is unnecessary overhead
Using nested loops which is inefficient
3. Consider the following buggy code for inserting an interval. Which line contains the subtle bug that can cause incorrect output when the input list is empty?
from typing import List
def insert(intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]:
intervals.append(newInterval)
intervals.sort(key=lambda x: x[0])
merged = [intervals[0]]
for i in range(1, len(intervals)):
if intervals[i][0] <= merged[-1][1]:
merged[-1][1] = max(merged[-1][1], intervals[i][1])
else:
merged.append(intervals[i])
return merged
medium
A. Line 5: merged = [intervals[0]]
B. Line 3: intervals.append(newInterval)
C. Line 7: if intervals[i][0] <= merged[-1][1]:
D. Line 9: merged.append(intervals[i])
Solution
Step 1: Consider empty intervals input
If intervals is empty, after appending newInterval, intervals has length 1.
Step 2: Check initialization of merged
Line 5 accesses intervals[0] without checking if intervals is empty before append, which is safe here but if input was empty, intervals[0] is newInterval, so no crash.
Step 3: Identify subtle bug
Actually, no crash here, but if input intervals was empty, sorting and merging still works. The subtle bug is that if intervals was empty and newInterval is appended, the code works, but if the initial intervals list is empty and the code did not append newInterval first, line 5 would crash. So the bug is that the code assumes intervals is non-empty before append, which is not always true.
Final Answer:
Option A -> Option A
Quick Check:
Accessing intervals[0] without empty check is risky [OK]
Hint: Accessing intervals[0] without empty check causes bug [OK]
Common Mistakes:
Not handling empty input intervals list
Assuming intervals always non-empty before append
Incorrect merging when intervals just touch
4. What is the time complexity of the sweep line algorithm for finding the minimum number of platforms required given n trains, and why?
medium
A. O(n log n) due to sorting arrivals and departures separately and merging them.
B. O(n^2) because each event is compared with all others to count overlaps.
C. O(n) because we only iterate through arrivals and departures once.
D. O(n log n) because events are sorted and then processed in a single pass.
Solution
Step 1: Identify sorting as the main cost
We create 2n events and sort them by time, which takes O(n log n).
Step 2: Single pass processing after sorting
After sorting, we iterate through events once, O(n), updating counts.
Final Answer:
Option A -> Option A
Quick Check:
Sorting dominates, linear sweep after [OK]
Hint: Sorting events dominates time complexity [OK]
Common Mistakes:
Assuming O(n) because of single pass ignoring sorting
Confusing nested loops brute force with sweep line
Thinking sorting arrivals and departures separately is enough
5. Suppose trains can arrive and depart multiple times (reusing platforms) and the schedule may include negative time values (e.g., early morning times). Which modification to the sweep line algorithm ensures correct minimum platform calculation?
hard
A. Use the same sweep line approach but ensure sorting handles negative times correctly and process events with departure before arrival on tie.
B. Use dynamic programming to handle multiple arrivals and departures per train.
C. Switch to nested loops checking overlaps since negative times break sorting assumptions.
D. Ignore negative times and treat them as zero to simplify sorting.
Solution
Step 1: Recognize that negative times do not affect event sorting if handled correctly
Sorting supports negative values naturally; no need to ignore or clamp them.
Step 2: Maintain departure-before-arrival ordering on tie to avoid overcounting
This ordering is critical regardless of time values or multiple arrivals/departures.
Step 3: Continue using sweep line approach for efficiency and correctness
Nested loops or DP are inefficient or unnecessary for this variant.
Final Answer:
Option A -> Option A
Quick Check:
Sweep line with correct sorting handles negative and multiple events [OK]
Hint: Sorting handles negative times; keep departure before arrival on tie [OK]
Common Mistakes:
Ignoring negative times or clamping them
Switching to inefficient nested loops unnecessarily