You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
API Kernel: IntervalMerge, IntervalSchedulingCore Mechanism: Process intervals by sorting and then merging or selecting based on overlap relationships.
This document presents the canonical interval templates covering merge, insert, overlap detection, greedy selection, and intersection problems. Each implementation follows consistent naming conventions and includes detailed logic explanations.
# Standard interval formatintervals: list[list[int]] = [[1, 3], [2, 6], [8, 10], [15, 18]]
# Each interval: [start, end] where start <= end# Interval with open/closed boundaries# [1, 3] = closed interval, includes both endpoints# (1, 3) = open interval, excludes both endpoints# Most LeetCode problems use closed intervals
1.2 Sorting Strategy
# Sort by start time (most common)intervals.sort(key=lambdax: x[0])
# Sort by end time (greedy selection problems)intervals.sort(key=lambdax: x[1])
# Sort by start, then by end (for tie-breaking)intervals.sort(key=lambdax: (x[0], x[1]))
1.3 Overlap Detection
# Two intervals [a_start, a_end] and [b_start, b_end] overlap if:# NOT (a_end < b_start OR b_end < a_start)# Equivalently:# a_start <= b_end AND b_start <= a_enddefoverlaps(a: list[int], b: list[int]) ->bool:
returna[0] <=b[1] andb[0] <=a[1]
# After sorting by start, simplified check:# Current interval starts before previous endsdefoverlaps_sorted(prev: list[int], curr: list[int]) ->bool:
returncurr[0] <=prev[1]
Problem: Merge all overlapping intervals.
Invariant: After sorting, overlapping intervals are adjacent.
Role: BASE TEMPLATE for interval merge pattern.
2.1 Pattern Recognition
Signal
Indicator
"merge overlapping"
→ Sort by start, extend end
"combine intervals"
→ IntervalMerge kernel
"non-overlapping result"
→ Merge adjacent overlaps
2.2 Implementation
# Pattern: interval_merge# See: docs/patterns/interval/templates.md Section 1 (Base Template)classSolution:
defmerge(self, intervals: List[List[int]]) ->List[List[int]]:
""" Merge overlapping intervals. Key Insight: - After sorting by start time, overlapping intervals are adjacent - Maintain current interval, extend end if overlap, else add new - Overlap check: current[0] <= prev_end (since sorted) Why sort by start? - All intervals starting before current end must overlap - No need to look backward after sorting """ifnotintervals:
return []
# Sort by start timeintervals.sort(key=lambdax: x[0])
merged: list[list[int]] = [intervals[0]]
forintervalinintervals[1:]:
# If current interval overlaps with last mergedifinterval[0] <=merged[-1][1]:
# Extend the endmerged[-1][1] =max(merged[-1][1], interval[1])
else:
# No overlap, add new intervalmerged.append(interval)
returnmerged
# Without sorting:# [[8,10], [1,3], [2,6]] → can't detect [1,3] overlaps [2,6]# After sorting by start:# [[1,3], [2,6], [8,10]] → adjacent overlaps are easy to detect
2.6 Complexity Analysis
Aspect
Complexity
Time
O(n log n) - dominated by sorting
Space
O(n) - worst case no overlaps
2.7 Related Problems
Problem
Variation
LC 57: Insert Interval
Insert then merge
LC 435: Non-overlapping Intervals
Min removals
LC 252: Meeting Rooms
Check any overlap
3. Insert Interval (LeetCode 57)
Problem: Insert a new interval into a sorted list of non-overlapping intervals.
Invariant: Three phases - before overlap, during overlap, after overlap.
Role: VARIANT of merge pattern with insertion.
3.1 Pattern Recognition
Signal
Indicator
"insert interval"
→ Three-phase processing
"sorted intervals"
→ Binary search optimization possible
"merge with new"
→ Track overlap region
3.2 Implementation
# Pattern: interval_insert# See: docs/patterns/interval/templates.md Section 2classSolution:
definsert(self, intervals: List[List[int]], newInterval: List[int]) ->List[List[int]]:
""" Insert new interval into sorted non-overlapping list. Key Insight: - Already sorted, process in three phases - Phase 1: Add all intervals ending before newInterval starts - Phase 2: Merge all overlapping intervals - Phase 3: Add all intervals starting after newInterval ends Why three phases? - Clean separation of logic - No need to re-sort after insertion """result: list[list[int]] = []
i=0n=len(intervals)
# Phase 1: Add all intervals that end before newInterval startswhilei<nandintervals[i][1] <newInterval[0]:
result.append(intervals[i])
i+=1# Phase 2: Merge overlapping intervalswhilei<nandintervals[i][0] <=newInterval[1]:
newInterval[0] =min(newInterval[0], intervals[i][0])
newInterval[1] =max(newInterval[1], intervals[i][1])
i+=1result.append(newInterval)
# Phase 3: Add remaining intervalswhilei<n:
result.append(intervals[i])
i+=1returnresult
3.3 Trace Example
Input: intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8]
Phase 1: intervals ending before 4
[1,2] ends at 2 < 4, add to result
result = [[1,2]]
Phase 2: intervals overlapping with [4,8]
[3,5]: 3 <= 8, merge → [3,8]
[6,7]: 6 <= 8, merge → [3,8]
[8,10]: 8 <= 8, merge → [3,10]
result = [[1,2], [3,10]]
Phase 3: remaining intervals
[12,16]: add
result = [[1,2], [3,10], [12,16]]
Output: [[1,2],[3,10],[12,16]]
3.4 Visual Representation
Input intervals:
[1-2]
[3--5]
[6-7]
[8--10]
[12----16]
New interval:
[4------8]
After insertion:
[1-2]
[3-----------10]
[12----16]
3.5 Edge Cases
# Empty intervalsintervals= [], newInterval= [5,7]
# Result: [[5,7]]# New interval before allintervals= [[3,5]], newInterval= [1,2]
# Result: [[1,2], [3,5]]# New interval after allintervals= [[1,2]], newInterval= [5,7]
# Result: [[1,2], [5,7]]# Complete overlapintervals= [[1,5]], newInterval= [2,3]
# Result: [[1,5]]
3.6 Complexity Analysis
Aspect
Complexity
Time
O(n) - single pass
Space
O(n) - output array
3.7 Related Problems
Problem
Variation
LC 56: Merge Intervals
Merge all overlapping
LC 715: Range Module
Add/remove ranges
LC 352: Data Stream as Disjoint Intervals
Dynamic insertion
4. Non-overlapping Intervals (LeetCode 435)
Problem: Find minimum number of intervals to remove for no overlaps.
Invariant: Sort by end time, greedily keep earliest-ending intervals.
Role: BASE TEMPLATE for interval scheduling (greedy selection).
4.1 Pattern Recognition
Signal
Indicator
"minimum removals"
→ Total - max non-overlapping
"no overlapping"
→ Greedy interval scheduling
"maximum non-overlapping"
→ Sort by end, greedy select
4.2 Implementation
# Pattern: interval_scheduling# See: docs/patterns/interval/templates.md Section 3 (Greedy Selection)classSolution:
deferaseOverlapIntervals(self, intervals: List[List[int]]) ->int:
""" Minimum intervals to remove for no overlaps. Key Insight: - Equivalent to: total - max non-overlapping intervals - Greedy: always keep interval that ends earliest - Sort by END time (not start!) for optimal selection Why sort by end? - Earlier ending = more room for future intervals - Greedy choice property: locally optimal → globally optimal """ifnotintervals:
return0# Sort by end time (critical for greedy!)intervals.sort(key=lambdax: x[1])
# Greedy selection: count non-overlappingnon_overlapping=1prev_end=intervals[0][1]
foriinrange(1, len(intervals)):
# If current starts after previous ends (no overlap)ifintervals[i][0] >=prev_end:
non_overlapping+=1prev_end=intervals[i][1]
# Else: skip current (implicit removal)returnlen(intervals) -non_overlapping
Sort by start (WRONG):
[1---------3] ← picked first
[2-3] ← can't pick (overlaps)
[3-4] ← can pick
Result: 2 intervals
Sort by end (CORRECT):
[1-2] ← picked first (ends earliest)
[2-3] ← can pick
[3-4] ← can pick
[1---------3] ← can't pick (overlaps with [1,2] and [2,3])
Result: 3 intervals
4.5 Alternative: Count Overlaps Directly
deferaseOverlapIntervals(self, intervals: List[List[int]]) ->int:
"""Count removals directly instead of subtracting."""ifnotintervals:
return0intervals.sort(key=lambdax: x[1])
removals=0prev_end=intervals[0][1]
foriinrange(1, len(intervals)):
ifintervals[i][0] <prev_end:
# Overlap detected, remove current (count it)removals+=1else:
# No overlap, update prev_endprev_end=intervals[i][1]
returnremovals
4.6 Complexity Analysis
Aspect
Complexity
Time
O(n log n) - dominated by sorting
Space
O(1) - excluding sort space
4.7 Related Problems
Problem
Variation
LC 452: Minimum Arrows
Max non-overlapping (balloons)
LC 646: Maximum Length of Pair Chain
Similar greedy
LC 1235: Maximum Profit in Job Scheduling
Weighted interval
5. Minimum Number of Arrows to Burst Balloons (LeetCode 452)
Problem: Find minimum arrows to burst all balloons (overlapping groups).
Invariant: One arrow bursts all balloons in an overlapping region.
Role: VARIANT of interval scheduling (count overlapping groups).
5.1 Pattern Recognition
Signal
Indicator
"minimum arrows/points"
→ Count overlapping groups
"burst all"
→ Cover all intervals
"one arrow per group"
→ Greedy grouping
5.2 Implementation
# Pattern: interval_scheduling (group counting variant)# See: docs/patterns/interval/templates.md Section 4classSolution:
deffindMinArrowPoints(self, points: List[List[int]]) ->int:
""" Minimum arrows to burst all balloons. Key Insight: - Equivalent to counting non-overlapping groups - Sort by end, greedily extend groups - New arrow needed when balloon starts after current arrow position Why this works: - Arrow at x bursts all balloons where start <= x <= end - Greedy: shoot at rightmost safe position (end of first balloon) """ifnotpoints:
return0# Sort by end positionpoints.sort(key=lambdax: x[1])
arrows=1arrow_pos=points[0][1] # Shoot at end of first balloonforiinrange(1, len(points)):
# If balloon starts after current arrow positionifpoints[i][0] >arrow_pos:
arrows+=1arrow_pos=points[i][1]
returnarrows
5.3 Trace Example
Input: [[10,16], [2,8], [1,6], [7,12]]
After sorting by end:
[[1,6], [2,8], [7,12], [10,16]]
Process:
1. arrows = 1, arrow_pos = 6
Shoot at x=6, bursts [1,6] and [2,8]
2. [2,8]: 2 <= 6, already burst
3. [7,12]: 7 > 6, need new arrow
arrows = 2, arrow_pos = 12
Shoot at x=12, bursts [7,12] and [10,16]
4. [10,16]: 10 <= 12, already burst
Output: 2
5.4 Visual Representation
Balloons (sorted by end):
[1------6]
[2--------8]
[7--------12]
[10------16]
Arrow 1 at x=6:
[1------6] ← burst
[2----X---8] ← burst (6 is within [2,8])
Arrow 2 at x=12:
[7--------12] ← burst
[10--X--16] ← burst
Total: 2 arrows
5.5 Connection to Non-overlapping Intervals
# LC 435: eraseOverlapIntervals# Remove minimum intervals for no overlaps# Answer: n - max_non_overlapping# LC 452: findMinArrowPoints# Minimum arrows (= number of overlapping groups)# Answer: max_non_overlapping (same greedy!)# Key difference:# - LC 435: count overlaps (n - groups)# - LC 452: count groups directly
5.6 Edge Cases
# Single balloonpoints= [[1,2]]
# Result: 1# All overlappingpoints= [[1,10], [2,5], [3,8]]
# Result: 1 (one arrow at x=5)# No overlappingpoints= [[1,2], [3,4], [5,6]]
# Result: 3
5.7 Complexity Analysis
Aspect
Complexity
Time
O(n log n) - dominated by sorting
Space
O(1) - excluding sort space
5.8 Related Problems
Problem
Variation
LC 435: Non-overlapping Intervals
Count removals instead
LC 253: Meeting Rooms II
Max concurrent
LC 56: Merge Intervals
Merge groups
6. Interval List Intersections (LeetCode 986)
Problem: Find intersections of two sorted interval lists.
Invariant: Two-pointer merge with intersection detection.
Role: VARIANT applying two-pointer technique to intervals.
6.1 Pattern Recognition
Signal
Indicator
"two sorted lists"
→ Two-pointer merge
"find intersections"
→ Compute overlap region
"common intervals"
→ max(starts), min(ends)
6.2 Implementation
# Pattern: interval_intersection# See: docs/patterns/interval/templates.md Section 5classSolution:
defintervalIntersection(
self, firstList: List[List[int]], secondList: List[List[int]]
) ->List[List[int]]:
""" Find all intersections of two sorted interval lists. Key Insight: - Use two pointers (like merge sort) - Intersection exists if max(starts) <= min(ends) - Advance pointer with smaller end (exhausted earlier) Why advance smaller end? - Interval with smaller end can't intersect future intervals - The other interval might still intersect with next intervals """result: list[list[int]] = []
i, j=0, 0whilei<len(firstList) andj<len(secondList):
a_start, a_end=firstList[i]
b_start, b_end=secondList[j]
# Check for intersectionstart=max(a_start, b_start)
end=min(a_end, b_end)
ifstart<=end:
result.append([start, end])
# Advance pointer with smaller endifa_end<b_end:
i+=1else:
j+=1returnresult
O(m + n) - each pointer advances at most once per interval
Space
O(min(m,n)) - intersection list (worst case)
6.7 Related Problems
Problem
Variation
LC 56: Merge Intervals
Merge instead of intersect
LC 88: Merge Sorted Array
Same two-pointer idea
LC 349: Intersection of Two Arrays
Set intersection
7. Pattern Comparison
7.1 Sort Key Decision
Sort By
When to Use
Problems
Start time
Merge overlapping, insert interval
LC 56, 57
End time
Greedy selection, min removals
LC 435, 452
Both pointers
Two sorted lists
LC 986
7.2 Overlap Definition Variants
# Standard overlap (closed intervals)# Intervals [a,b] and [c,d] overlap if: a <= d AND c <= b# After sorting by start:# Current overlaps with previous if: curr_start <= prev_end# After sorting by end:# Current overlaps with previous if: curr_start < prev_end# Note: < not <= because we're greedy (want equality to mean "touch")
START: Given interval problem
│
├─ "Merge" or "combine" intervals?
│ └─ YES → Sort by START, merge adjacent
│ (LC 56 pattern)
│
├─ "Insert" new interval into sorted list?
│ └─ YES → Three-phase processing
│ (LC 57 pattern)
│
├─ "Remove minimum" for no overlaps?
│ └─ YES → Sort by END, greedy count
│ Answer = n - non_overlapping
│ (LC 435 pattern)
│
├─ "Minimum arrows/points" to cover all?
│ └─ YES → Sort by END, count groups
│ (LC 452 pattern)
│
├─ "Intersection" of two sorted lists?
│ └─ YES → Two-pointer merge
│ (LC 986 pattern)
│
└─ None of above?
└─ Consider: Line sweep, Meeting rooms variant
8.2 Sort Strategy Selection
Need to MERGE intervals?
→ Sort by START
→ Reason: Adjacent overlaps become consecutive
Need to SELECT maximum non-overlapping?
→ Sort by END
→ Reason: Greedy - earliest end leaves most room
Need to INTERSECT two lists?
→ Already sorted, use TWO POINTERS
→ Reason: Linear merge technique