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
# 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]))
# 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.
# 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
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.
# 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
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).
# 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
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).
# 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
# 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
# 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
# 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")
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
definterval_intersection(A:List[List[int]],B:List[List[int]])->List[List[int]]:"""Two-pointer merge for intersections."""result=[]i,j=0,0whilei<len(A)andj<len(B):start=max(A[i][0],B[j][0])end=min(A[i][1],B[j][1])ifstart<=end:result.append([start,end])ifA[i][1]<B[j][1]:i+=1else:j+=1returnresult
defoverlaps(a:List[int],b:List[int])->bool:"""Check if two intervals overlap (closed intervals)."""returna[0]<=b[1]andb[0]<=a[1]defmerge_two(a:List[int],b:List[int])->List[int]:"""Merge two overlapping intervals."""return[min(a[0],b[0]),max(a[1],b[1])]defintersection(a:List[int],b:List[int])->Optional[List[int]]:"""Get intersection of two intervals, or None."""start=max(a[0],b[0])end=min(a[1],b[1])return[start,end]ifstart<=endelseNone