Understand
30-second explanation
Build the interview-ready mental model before comparing implementations.
A person can attend every meeting only if no two meetings overlap. Sort by start time, then check adjacent pairs: if any meeting starts before the previous one ends, return false. After sorting, the only meeting that can conflict with the current one is its immediate predecessor — so one linear scan suffices. O(n log n) sort, O(n) scan.
Interview contextWhat is testedWhere candidates slipHow to stand outExplore interview contextHide interview context
What's being tested
Whether you realize sorting by start reduces 'do any two overlap?' from O(n²) pair-checking to a single neighbor scan.
Where candidates slip
Comparing every pair (O(n²)) instead of sorting, or getting the boundary wrong so a meeting starting exactly when another ends is flagged as a conflict.
How to stand out
Explain why adjacency-after-sorting is sufficient, use strict `<` for the touch-is-ok semantics, and note this is the warm-up to Meeting Rooms II where you count rooms instead of returning a boolean.
Choose
Compare the available approaches
Use constraints and trade-offs to select the route you can defend in an interview.
Interactive decision treeInteractiveChoose the right approachFirst decision previewQuestion about meeting intervalsYes/no, count rooms, or merge?Count roomsMergeExplore the full treeCollapse the tree
Learn & trace
Build each solution step by step
Move from the core idea to a visual trace, implementation, proof, and edge cases.
Approach 1 of 2
Brute Force — Check Every Pair
1Hint 1Explore hintHide hint
Two meetings conflict iff a.start < b.end AND b.start < a.end. What's the most direct way to check all of them?
2Hint 2Explore hintHide hint
Compare every unordered pair (i, j) with i < j; return false on the first overlap.
3Hint 3Explore hintHide hint
It's correct but O(n²). Is there an ordering that makes only neighbors worth comparing?
A · Core idea
The literal reading: a person can attend all meetings unless some two of them overlap, so check all pairs.
The overlap definition. Two meetings a and b overlap iff a.start < b.end && b.start < a.end — each starts before the other ends. Strict < encodes the 'touching is fine' rule. Loop over every pair and return false the moment one overlaps.
Why we don't ship it. With n up to 10⁴ that's ~50 million comparisons — passable, but it ignores structure. Sorting by start guarantees that if meeting i overlaps anything earlier, it overlaps meeting i−1 specifically, so comparing all pairs is wasteful. State this, then sort.
B · Visual dry run
Interactive dry run
Select a frame to connect the decision with its visual state.
intervals = [[0,30],[5,10],[15,20]](0,30)&(5,10)
0 < 10 and 5 < 30 → overlap
return falsefirst conflict ends it
falseC · Code with teaching comments
Reference implementation previewpublic boolean canAttendMeetings(int[][] intervals) { int n = intervals.length; for (int i = 0; i < n; i++) { for (int j = i + 1; j < n; j++) { if (intervals[i][0] < intervals[j][1] &&Explore the full codeHide the full code
D · Why it works and complexityO(n²) timeO(1) spaceOpen the invariant and proofExplore the proofHide the proof
All-pairs checking is the honest baseline, but it throws away the fact that overlap is a local property once you order by time. Sorting turns a global pairwise question into a local neighbor question.
ComplexityO(n²) time, O(1) space — n(n−1)/2 pair comparisons in the worst case; no extra storage. Sorting cuts this to O(n) comparisons after an O(n log n) sort.
E · Edge cases and pitfalls5intervals = [] — Outer loop never runs → returns true (nothing to attend).Explore edge casesHide edge cases
Edge cases handled
intervals = []Outer loop never runs → returns true (nothing to attend).intervals = [[0,30],[30,60]]0 < 60 but 30 < 30 is false → not an overlap → returns true.
Pitfalls
- Using `<=` flips the touching rule and wrongly reports back-to-back meetings as conflicts.
- O(n²) is needless when sorting gives O(n) comparisons.
- Forgetting to return early — no need to keep scanning after the first conflict.
Approach 2 of 2 · Optimal
Sort by Start, Scan Adjacent (Optimal)
1Hint 1Explore hintHide hint
If meetings were sorted by start time, which earlier meeting is the only one that could still be running when the current one begins?
2Hint 2Explore hintHide hint
Sort by start. Walk from the second meeting; if its start is before the previous meeting's end, there's a conflict.
3Hint 3Explore hintHide hint
Return false on the first conflict; if you reach the end, return true.
A · Core idea
Sort the meetings by start time, then scan once comparing each meeting to its immediate predecessor.
Why adjacency is enough. After sorting by start, the meetings are in non-decreasing start order. Suppose meeting i overlaps some earlier meeting k < i. Because starts are sorted, meeting i−1 also starts no later than i and no earlier than k, and the earliest-ending relevant boundary to worry about is the one right before i. Concretely: if meetings[i].start < meetings[i-1].end, they overlap; if not, i starts at or after i−1 ends, and since every earlier meeting ends no later than... — the cleanest framing is that once sorted, a conflict always shows up between some adjacent pair, so checking all adjacent pairs catches every conflict. No all-pairs loop needed.
The conflict test. meetings[i].start < meetings[i-1].end with strict <. Equality (start == prevEnd) is back-to-back, which is allowed. The instant the test is true, return false; if the scan completes, return true.
Why a boolean, not a merge. Unlike Merge Intervals, we don't build anything — we just need existence of a conflict, so we short-circuit on the first one. That's what keeps this an 'easy': same sort, simpler payload.
B · Visual dry run
Interactive dry run
Select a frame to connect the decision with its visual state.
intervals = [[0,30],[5,10],[15,20]] (sorted by start: same order)Array state
i=1
[5,10] start 5 < 30 → conflict
falseC · Code with teaching comments
D · Why it works and complexityO(n log n) timeO(1) spaceOpen the invariant and proofExplore the proofHide the proof
Sorting localizes the question: 'do ANY two overlap?' becomes 'does any meeting start before its predecessor ends?'. The same sort-then-neighbor move underlies Merge Intervals — the difference is Meeting Rooms answers yes/no and stops early, while Merge builds the fused list. Recognizing they're the same skeleton makes both automatic.
ComplexityO(n log n) time, O(1) space — Sorting is O(n log n) and dominates; the single neighbor scan is O(n). Sorting in place keeps extra space at O(1) (ignoring sort internals).
E · Edge cases and pitfalls5intervals = [[7,10],[2,4]] — Sorted → [2,4],[7,10]; 7 < 4 is false → no conflict → true.Explore edge casesHide edge cases
Edge cases handled
intervals = [[7,10],[2,4]]Sorted → [2,4],[7,10]; 7 < 4 is false → no conflict → true.intervals = [[0,30],[30,60],[60,90]]Each starts exactly when the prior ends; 30<30, 60<60 both false → true.
Pitfalls
- Forgetting to sort — adjacent comparison is only valid on start-sorted input.
- Using `<=` instead of `<` — flags valid back-to-back meetings as conflicts.
- Comparing to a running max-end instead of the immediate predecessor (works, but unnecessary here; predecessor end is non-decreasing after sorting by start only if no overlaps — when overlaps exist you've already returned false).
Practice
Try the solution yourself
Edit the optimal implementation, test another input, and observe the result.
Editable code previewpublic boolean canAttendMeetings(int[][] intervals) { Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0])); for (int i = 1; i < intervals.length; i++) { if (intervals[i][0] < intervals[i - 1][1]) { return false;Explore the playgroundClose the playground
Avoid mistakes
Recognize the interview traps
Understand why common attempts fail before reviewing the corrected implementation.
1Wrong boundary on touching meetings
A meeting ending at t and another starting at t do not conflict. Using <= reports [0,30] and [30,60] as overlapping and returns false incorrectly.
Explore the code comparisonHide the code comparison
Wrong boundary on touching meetings
A meeting ending at t and another starting at t do not conflict. Using <= reports [0,30] and [30,60] as overlapping and returns false incorrectly.
Don't write this
if intervals[i][0] <= intervals[i - 1][1]: # flags back-to-back
return FalseWrite this instead
if intervals[i][0] < intervals[i - 1][1]: # touching is allowed
return False2Not sorting before the neighbor scan
The adjacent comparison only catches all conflicts when meetings are ordered by start. On unsorted input a conflict can sit between non-adjacent entries and be missed.
Explore the code comparisonHide the code comparison
Not sorting before the neighbor scan
The adjacent comparison only catches all conflicts when meetings are ordered by start. On unsorted input a conflict can sit between non-adjacent entries and be missed.
Don't write this
for (int i = 1; i < intervals.length; i++) { // no sort
if (intervals[i][0] < intervals[i - 1][1]) return false;
}Write this instead
Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0]));
for (int i = 1; i < intervals.length; i++) {
if (intervals[i][0] < intervals[i - 1][1]) return false;
}Revise
Carry the pattern into the interview
Pattern to remember
Sort by start, check adjacent overlap
Code pattern to memorize
meetings.sort(by start)
for i in 1..n-1:
if meetings[i].start < meetings[i-1].end: return False
return TrueRules to remember
- 1Sort by start first. Then overlaps can only be between neighbors — no need to compare all pairs.
- 2Conflict test is
start[i] < end[i-1](strict<): a meeting that begins exactly when the prior ends is fine. - 3You want a boolean, not a merge — return false the instant you find one conflict.
- 4Compare each meeting to the immediate predecessor in sorted order, not to a running 'max end'.
- 5Empty or single-meeting input → trivially true.
Use this pattern when
- 'Can one person/resource attend/handle ALL intervals?' yes/no question
- Detect whether ANY two intervals overlap
- Single-resource feasibility check
- You need a boolean, not a count or a merged set
Choose another pattern when
- 'How many rooms?' → max concurrency, min-heap or sweep (LC 253)
- 'Merge the overlaps' → sort by start, sweep-merge (LC 56)
- 'Remove fewest to make disjoint' → greedy by end (LC 435)
- Endpoints are inclusive (a meeting [1,2] blocks another starting at 2) → use `<=`
Interview takeaway
When you only need to know IF intervals collide, sort by start and look at neighbors — a single adjacent overlap is enough to answer no. It's the lightweight cousin of Merge Intervals: same sort, but you short-circuit on the first conflict instead of building a merged list.
Continue
Transfer the pattern to another problem
Use a direct variation or pattern sibling to prove the idea is reusable.
Transferable patternOverlap-Detection Sweep. Sort by start, then a conflict can only be adjacent — short-circuit on the first. The boolean cousin of Merge Intervals and the warm-up to Meeting Rooms II.
Practice queueDirect variations and pattern siblings
Direct follow-ups
- Meeting Rooms IINow count the minimum rooms = maximum concurrent meetings. Min-heap of end times.
- Merge IntervalsSame sort-by-start skeleton, but build the merged list instead of returning a boolean.
- Insert IntervalInsert one interval into a sorted, disjoint list in O(n).
- Non-overlapping IntervalsIf they CAN'T all be attended, how few would you drop? Greedy by end.