Coding problem · Medium · Intervals
Find Free Meeting Slots
Medium Practical tasks Seen at Google, Microsoft, Calendly, Atlassian
A calendar service stores busy blocks as [start, end) in minutes from midnight. Blocks may overlap and are unsorted.
Given busy, the working day dayStart/dayEnd, and minLength, return all free slots [start, end) within the working day whose length is at least minLength, in chronological order. Busy blocks may extend outside the working day.
Example 1
Input: busy = [[540,600],[570,630],[720,780]], dayStart = 540, dayEnd = 1020, minLength = 30 Output: [[630,720],[780,1020]] Explanation: 09:00–10:30 busy (merged), lunch 12:00–13:00.
Hints
Hint 1
Sort busy blocks by start and sweep with a cursor free_from = dayStart.
Hint 2
Each block either leaves a gap before it (start - free_from >= minLength) or extends the cursor to max(cursor, end).
Write and run your solution
Loading the editor… (JavaScript is required to run code in your browser.)
Solution and explanation
Show the solution
Time O(n log n) · Space O(n)
Sort by start, keep cursor = the earliest time not known to be busy (starting at dayStart). For each block, if it starts after the cursor, the gap [cursor, min(start, dayEnd)) is free — keep it if long enough; then move the cursor to max(cursor, end). Finally, the tail [cursor, dayEnd) is free. Clamping to the working day handles blocks that spill outside it.
Reference solution
function freeSlots(busy, dayStart, dayEnd, minLength) {
const blocks = [...busy].sort((a, b) => a[0] - b[0]);
const out = [];
let cursor = dayStart;
for (const [s, e] of blocks) {
const gapEnd = Math.min(s, dayEnd);
if (gapEnd - cursor >= minLength) out.push([cursor, gapEnd]);
cursor = Math.max(cursor, e);
if (cursor >= dayEnd) break;
}
if (dayEnd - cursor >= minLength) out.push([cursor, dayEnd]);
return out;
}
function freeSlots(busy: number[][], dayStart: number, dayEnd: number, minLength: number): number[][] {
const out: number[][] = [];
let cursor = dayStart;
for (const [s, e] of [...busy].sort((a, b) => a[0] - b[0])) {
const gapEnd = Math.min(s, dayEnd);
if (gapEnd - cursor >= minLength) out.push([cursor, gapEnd]);
cursor = Math.max(cursor, e);
}
if (dayEnd - cursor >= minLength) out.push([cursor, dayEnd]);
return out;
}
def free_slots(busy: list[list[int]], day_start: int, day_end: int, min_length: int) -> list[list[int]]:
out: list[list[int]] = []
cursor = day_start
for s, e in sorted(busy):
gap_end = min(s, day_end)
if gap_end - cursor >= min_length:
out.append([cursor, gap_end])
cursor = max(cursor, e)
if day_end - cursor >= min_length:
out.append([cursor, day_end])
return out
<?php
function freeSlots(array $busy, int $dayStart, int $dayEnd, int $minLength): array
{
usort($busy, fn($a, $b) => $a[0] <=> $b[0]);
$out = [];
$cursor = $dayStart;
foreach ($busy as [$s, $e]) {
$gapEnd = min($s, $dayEnd);
if ($gapEnd - $cursor >= $minLength) {
$out[] = [$cursor, $gapEnd];
}
$cursor = max($cursor, $e);
}
if ($dayEnd - $cursor >= $minLength) {
$out[] = [$cursor, $dayEnd];
}
return $out;
}
class Solution {
public int[][] freeSlots(int[][] busy, int dayStart, int dayEnd, int minLength) {
int[][] blocks = busy.clone();
Arrays.sort(blocks, (a, b) -> Integer.compare(a[0], b[0]));
List<int[]> out = new ArrayList<>();
int cursor = dayStart;
for (int[] b : blocks) {
int gapEnd = Math.min(b[0], dayEnd);
if (gapEnd - cursor >= minLength) out.add(new int[]{cursor, gapEnd});
cursor = Math.max(cursor, b[1]);
}
if (dayEnd - cursor >= minLength) out.add(new int[]{cursor, dayEnd});
return out.toArray(new int[0][]);
}
}
Follow-up questions
- Find slots free for all attendees (merge everyone's busy blocks first).
- Handle time zones and recurring events.
More Intervals problems
- Merge Intervals · Medium
- Insert Interval · Medium