Employee Free Time asks: given everyone's busy schedules, when is everyone free at the same time? It is a classic interval-merging problem — the same sweep you use to merge meeting times, dressed up for a team.
Problem. You are given schedule, a list of employees, where each employee is a list of busy
intervals [start, end] (sorted and non-overlapping per employee). Return the list of finite
intervals during which all employees are free, in sorted order.
Example: schedule = [[[1,3],[6,7]], [[2,4],[8,10]]] → answer [[4,6], [7,8]]. Everyone is busy
somewhere in [1,4], free in [4,6], busy in [6,7], free in [7,8], busy in [8,10].
The slow way first
You could build a minute-by-minute timeline and mark every busy slot, then scan for the unmarked stretches. That works for small numbers but blows up: the time range can be huge, so a per-unit sweep is wasteful and only handles integer times. We want something that depends on the number of intervals, not the size of the clock.
The question to ask: if I lined up every busy interval on one timeline, where would the gaps be? The gaps between the combined busy blocks are exactly the free time.
The idea: flatten, sort, merge, read the gaps
Forget which employee owns which interval — free time only cares about when someone is busy. So:
- Flatten every employee's intervals into one big list.
- Sort that list by start time, so overlapping blocks sit next to each other.
- Merge overlaps into combined busy blocks.
- Each gap between two consecutive merged blocks is a free interval.
The key insight: after sorting, you sweep left to right holding one current merged block. If the next interval starts at or before the current end, it overlaps — extend the end. If it starts later, the space in between is free, so emit it and start a new block.
Walk through it
Step through the animation. The four busy intervals are already flattened and sorted: [1,3], [2,4], [6,7], [8,10]. The pointer i sweeps right. [2,4] overlaps [1,3], so they merge into [1,4]. Then [6,7] starts after 4, so [4,6] is free and a new block begins. Finally [8,10] starts after 7, so [7,8] is free.
Pseudocode
intervals = flatten every employee schedule into one list
sort intervals by start
free = empty list
cur = first interval
for each next interval after the first:
if next.start <= cur.end: # overlap
cur.end = max(cur.end, next.end)
else: # a gap
free.append([cur.end, next.start])
cur = next
return freeThe Python solution
def employee_free_time(schedule):
intervals = [iv for emp in schedule for iv in emp]
intervals.sort(key=lambda iv: iv[0])
free = []
cur = intervals[0]
for nxt in intervals[1:]:
if nxt[0] <= cur[1]:
if nxt[1] > cur[1]:
cur[1] = nxt[1]
else:
free.append([cur[1], nxt[0]])
cur = nxt
return free- The list comprehension flattens every employee
empand every intervalivinto one list. - We sort by start (
iv[0]) so overlapping busy blocks line up next to each other. curis the current merged busy block. We seed it with the first interval.- Line 7 checks overlap: if
nxtstarts at or beforecur's end, they touch — extendcur's end ifnxtreaches further. - Otherwise (line 10) there is a gap: the stretch
[cur.end, nxt.start]is free, so we record it and movecurtonxt.
Complexity
| Case | Time | Notes |
|---|---|---|
| Flatten | O(n) (moderate) | n = total intervals across everyone |
| Sort | O(n log n) (moderate) | dominant cost |
| Merge sweep | O(n) (moderate) | one pass over sorted list |
O(n) (moderate)Sorting dominates, so the whole thing is O(n log n) time and O(n) space for the flattened list. (With a heap that merges already-sorted per-employee lists you can shave the constant, but the asymptotics are the same.)
When this pattern shows up
Whenever a problem gives you intervals and asks to merge them, find overlaps, or find the holes between them, the move is almost always sort by start, then sweep with one running block. Merge Intervals, Insert Interval, Meeting Rooms, and Employee Free Time are all the same sweep.
Only finite gaps count as free time. The open-ended stretch before the first block and after the last block is not returned — that is why you read gaps strictly between consecutive merged blocks, never before the first or after the last.
Practice
After merging [1,3] and [2,4] into [1,4], the next interval is [6,7]. Is there free time, and if so what is it?
1. Why do we sort the flattened intervals by start time?
2. When does the sweep emit a free interval?
3. What is the overall time complexity?
4. Why do we ignore the time before the first block and after the last?