🧠
Sweep Line / Event Processing
💡 This approach uses a sweep line technique to process interval start and end events, tracking how many employees are busy at each point, allowing direct detection of free intervals.
Intuition
Convert intervals into start and end events, sort them, then sweep through the timeline counting active intervals. When count drops to zero, it means all employees are free.
Algorithm
- Create a list of all start and end events from all intervals, marking start as +1 and end as -1.
- Sort all events by time; if times are equal, end events come before start events.
- Initialize a counter to track active intervals and a variable to track previous event time.
- Sweep through events, updating the counter; when counter is zero, record the gap between previous event and current event as free time.
💡 This approach efficiently tracks overlapping intervals without merging, directly identifying free gaps by counting active intervals.
from typing import List
def employeeFreeTime(schedule: List[List[List[int]]]) -> List[List[int]]:
events = []
for emp in schedule:
for interval in emp:
events.append((interval[0], 1)) # start event
events.append((interval[1], -1)) # end event
# Sort by time; end events before start if tie
events.sort(key=lambda x: (x[0], x[1]))
free_times = []
active = 0
prev = None
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
return free_times
# Driver code
if __name__ == '__main__':
schedule = [[[1,2],[5,6]],[[1,3]],[[4,10]]]
print(employeeFreeTime(schedule))
Line Notes
events = []Initialize list to hold all start and end events
events.append((interval[0], 1))Add start event with +1 to indicate interval begins
events.append((interval[1], -1))Add end event with -1 to indicate interval ends
events.sort(key=lambda x: (x[0], x[1]))Sort events by time; end events before start if times equal to avoid false free time
if active == 0 and prev is not None and prev < time:When no active intervals, record free time between prev and current event
active += e_typeUpdate active interval count based on event type
prev = timeUpdate previous event time for next iteration
import java.util.*;
public class EmployeeFreeTime {
public static List<int[]> employeeFreeTime(List<List<int[]>> schedule) {
List<int[]> events = new ArrayList<>();
for (List<int[]> emp : schedule) {
for (int[] interval : emp) {
events.add(new int[]{interval[0], 1}); // start event
events.add(new int[]{interval[1], -1}); // end event
}
}
events.sort((a, b) -> {
if (a[0] != b[0]) return a[0] - b[0];
return a[1] - b[1]; // end event (-1) before start event (1)
});
List<int[]> freeTimes = new ArrayList<>();
int active = 0;
Integer prev = null;
for (int[] event : events) {
int time = event[0], type = event[1];
if (active == 0 && prev != null && prev < time) {
freeTimes.add(new int[]{prev, time});
}
active += type;
prev = time;
}
return freeTimes;
}
public static void main(String[] args) {
List<List<int[]>> schedule = new ArrayList<>();
schedule.add(Arrays.asList(new int[]{1,2}, new int[]{5,6}));
schedule.add(Arrays.asList(new int[]{1,3}));
schedule.add(Arrays.asList(new int[]{4,10}));
List<int[]> result = employeeFreeTime(schedule);
for (int[] interval : result) {
System.out.println("[" + interval[0] + "," + interval[1] + "]");
}
}
}
Line Notes
List<int[]> events = new ArrayList<>();Collect all start and end events from intervals
events.add(new int[]{interval[0], 1});Add start event with +1 to indicate interval begins
events.add(new int[]{interval[1], -1});Add end event with -1 to indicate interval ends
events.sort((a, b) -> { ... });Sort events by time; end events before start if tie to avoid false free time
if (active == 0 && prev != null && prev < time) {Record free time when no active intervals between prev and current event
active += type;Update count of active intervals based on event type
prev = time;Update previous event time for next iteration
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
vector<vector<int>> employeeFreeTime(vector<vector<vector<int>>>& schedule) {
vector<pair<int,int>> events;
for (auto& emp : schedule) {
for (auto& interval : emp) {
events.emplace_back(interval[0], 1); // start event
events.emplace_back(interval[1], -1); // end event
}
}
sort(events.begin(), events.end(), [](const pair<int,int>& a, const pair<int,int>& b) {
if (a.first != b.first) return a.first < b.first;
return a.second < b.second; // end event (-1) before start event (1)
});
vector<vector<int>> freeTimes;
int active = 0;
int prev = -1;
bool first = true;
for (auto& e : events) {
int time = e.first, type = e.second;
if (active == 0 && !first && prev < time) {
freeTimes.push_back({prev, time});
}
active += type;
prev = time;
first = false;
}
return freeTimes;
}
int main() {
vector<vector<vector<int>>> schedule = {{{1,2},{5,6}},{{1,3}},{{4,10}}};
vector<vector<int>> result = employeeFreeTime(schedule);
for (auto& interval : result) {
cout << "[" << interval[0] << "," << interval[1] << "]\n";
}
return 0;
}
Line Notes
events.emplace_back(interval[0], 1);Add start event with +1 to indicate interval begins
events.emplace_back(interval[1], -1);Add end event with -1 to indicate interval ends
sort(events.begin(), events.end(), ...Sort events by time; end events before start if tie to avoid false free time
if (active == 0 && !first && prev < time) {Record free time when no active intervals between prev and current event
active += type;Update count of active intervals based on event type
prev = time;Update previous event time for next iteration
function employeeFreeTime(schedule) {
let events = [];
for (let emp of schedule) {
for (let interval of emp) {
events.push([interval[0], 1]); // start event
events.push([interval[1], -1]); // end event
}
}
events.sort((a, b) => a[0] === b[0] ? a[1] - b[1] : a[0] - b[0]);
let freeTimes = [];
let active = 0;
let prev = null;
for (let [time, type] of events) {
if (active === 0 && prev !== null && prev < time) {
freeTimes.push([prev, time]);
}
active += type;
prev = time;
}
return freeTimes;
}
// Driver code
const schedule = [[[1,2],[5,6]],[[1,3]],[[4,10]]];
console.log(employeeFreeTime(schedule));
Line Notes
events.push([interval[0], 1]);Add start event with +1 to indicate interval begins
events.push([interval[1], -1]);Add end event with -1 to indicate interval ends
events.sort((a, b) => a[0] === b[0] ? a[1] - b[1] : a[0] - b[0]);Sort events by time; end events before start if tie to avoid false free time
if (active === 0 && prev !== null && prev < time) {Record free time when no active intervals between prev and current event
active += type;Update count of active intervals based on event type
prev = time;Update previous event time for next iteration
TimeO(N log N) where N is total number of interval endpoints
SpaceO(N) for storing events and output
Sorting all 2N events dominates time; sweeping through events is linear.
💡 For 10,000 intervals, we have 20,000 events; sorting takes about 20,000 * log2(20,000) ≈ 280,000 operations, efficient for interviews.
Interview Verdict: Accepted
This is the optimal and most elegant approach, often preferred in interviews for interval union and gap problems.