Pattern family: Array / Interval Scheduling / Greedy Notes & Templates: 01_merge_interval_notes.md Language: Java
- P8 β Non-overlapping Intervals
- P9 β Minimum Number of Arrows to Burst Balloons
- P10 β Meeting Scheduler
- P11 β Employee Free Time
- P12 β Remove Covered Intervals
- P13 β Data Stream as Disjoint Intervals
LeetCode #56 | Difficulty: Medium | Company: Amazon, Google, Facebook | Category: Sort + Linear Scan
Given an array of intervals, merge all overlapping intervals and return the result.
Sort by start time. After sorting, overlapping intervals are always adjacent. Track start and end of current merged interval. When the next interval's start exceeds end, emit and reset.
| Step | Condition | Action |
|---|---|---|
| Sort | β | by start time |
| Init | β | start = intervals[0][0], end = intervals[0][1] |
| Overlap | intervals[i][0] <= end |
end = max(end, intervals[i][1]) |
| No overlap | intervals[i][0] > end |
emit [start, end], reset to intervals[i] |
| After loop | β | emit last [start, end] |
class Solution {
public int[][] merge(int[][] intervals) {
if (intervals.length == 0) return new int[0][0];
Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
List<int[]> res = new ArrayList<>();
int start = intervals[0][0];
int end = intervals[0][1];
for (int i = 1; i < intervals.length; i++) {
if (end >= intervals[i][0]) {
end = Math.max(end, intervals[i][1]);
} else {
res.add(new int[]{start, end});
start = intervals[i][0];
end = intervals[i][1];
}
}
res.add(new int[]{start, end});
return res.toArray(new int[res.size()][]);
}
}Note: Tracks
startandendas separate variables β clear and interview-friendly. Both solutions are identical in logic.
public int[][] merge(int[][] intervals) {
Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
List<int[]> res = new ArrayList<>();
int[] curr = intervals[0];
for (int i = 1; i < intervals.length; i++) {
if (intervals[i][0] <= curr[1]) {
curr[1] = Math.max(curr[1], intervals[i][1]); // extend in place
} else {
res.add(curr);
curr = intervals[i];
}
}
res.add(curr);
return res.toArray(new int[res.size()][]);
}Input: [[1,3],[2,6],[8,10],[15,18]]
Sorted: [[1,3],[2,6],[8,10],[15,18]]
start=1, end=3
i=1: [2,6] β 2<=3 β end=max(3,6)=6
i=2: [8,10] β 8>6 β emit [1,6], start=8,end=10
i=3: [15,18] β 15>10 β emit [8,10], start=15,end=18
After loop: emit [15,18]
Output: [[1,6],[8,10],[15,18]] β
LeetCode #57 | Difficulty: Medium | Company: Google, LinkedIn | Category: 3-Phase Insert
Insert a new interval into a sorted non-overlapping list, merging if necessary.
3-phase scan: (1) add all intervals ending before newInterval starts, (2) merge all overlapping into newInterval, (3) add remaining. No sorting needed β input is already sorted.
| Phase | Condition | Action |
|---|---|---|
| 1 β Before | intervals[i][1] < newInterval[0] |
add as-is, advance i |
| 2 β Overlap | intervals[i][0] <= newInterval[1] |
expand newInterval, advance i |
| Add | β | add merged newInterval |
| 3 β After | remaining | add as-is |
class Solution {
public int[][] insert(int[][] intervals, int[] newInterval) {
List<int[]> res = new ArrayList<>();
int i = 0;
int n = intervals.length;
// 1οΈβ£ Add all intervals before newInterval
while (i < n && intervals[i][1] < newInterval[0]) {
res.add(intervals[i]);
i++;
}
// 2οΈβ£ Merge overlapping intervals
while (i < n && intervals[i][0] <= newInterval[1]) {
newInterval[0] = Math.min(newInterval[0], intervals[i][0]);
newInterval[1] = Math.max(newInterval[1], intervals[i][1]);
i++;
}
res.add(newInterval);
// 3οΈβ£ Add remaining intervals
while (i < n) {
res.add(intervals[i]);
i++;
}
return res.toArray(new int[res.size()][]);
}
}Note: The 3-phase structure with comments is exactly the right way to write this in an interview β clear phases, easy to trace. Reference condenses into one loop but is harder to explain.
public int[][] insert(int[][] intervals, int[] newInterval) {
List<int[]> res = new ArrayList<>();
int i = 0, n = intervals.length;
while (i < n && intervals[i][1] < newInterval[0]) res.add(intervals[i++]);
while (i < n && intervals[i][0] <= newInterval[1]) {
newInterval[0] = Math.min(newInterval[0], intervals[i][0]);
newInterval[1] = Math.max(newInterval[1], intervals[i][1]);
i++;
}
res.add(newInterval);
while (i < n) res.add(intervals[i++]);
return res.toArray(new int[res.size()][]);
}intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8]
Phase 1: [1,2] ends at 2 < 4 β add. [3,5] ends at 5 >= 4 β stop. res=[[1,2]]
Phase 2: [3,5]: 3<=8 β new=[min(4,3),max(8,5)]=[3,8]
[6,7]: 6<=8 β new=[min(3,6),max(8,7)]=[3,8]
[8,10]: 8<=8 β new=[min(3,8),max(8,10)]=[3,10]
[12,16]: 12>8 β stop. add [3,10]
Phase 3: add [12,16]
Output: [[1,2],[3,10],[12,16]] β
LeetCode #986 | Difficulty: Medium | Company: Facebook, Amazon | Category: Two-pointer
Given two sorted lists of intervals, return their intersection.
Two pointers i and j. Check if current pair intersects: max(start1,start2) <= min(end1,end2). Advance the pointer whose interval ends first β it can't intersect with any future interval from the other list.
| Step | Condition | Action |
|---|---|---|
| Intersect check | max(s1,s2) <= min(e1,e2) |
add [max(s1,s2), min(e1,e2)] |
| Advance | end1 <= end2 |
i++ |
| Advance | end1 > end2 |
j++ |
class Solution {
public int[][] intervalIntersection(int[][] firstList, int[][] secondList) {
List<int[]> res = new ArrayList<>();
int i = 0, j = 0;
while (i < firstList.length && j < secondList.length) {
int start1 = firstList[i][0], end1 = firstList[i][1];
int start2 = secondList[j][0], end2 = secondList[j][1];
if (start1 <= start2) {
if (end1 >= start2) {
int start = Math.max(start1, start2);
int end = Math.min(end1, end2);
res.add(new int[]{start, end});
}
} else {
if (end2 >= start1) {
int start = Math.max(start1, start2);
int end = Math.min(end1, end2);
res.add(new int[]{start, end});
}
}
if (end1 <= end2) i++;
else j++;
}
return res.toArray(new int[res.size()][]);
}
}Note: Splits the check into two cases (start1 <= start2 vs start2 < start1). Reference unifies both cases into a single
max/mincheck which is cleaner β both produce the same result.
public int[][] intervalIntersection(int[][] A, int[][] B) {
List<int[]> res = new ArrayList<>();
int i = 0, j = 0;
while (i < A.length && j < B.length) {
int start = Math.max(A[i][0], B[j][0]);
int end = Math.min(A[i][1], B[j][1]);
if (start <= end) res.add(new int[]{start, end}); // valid intersection
if (A[i][1] <= B[j][1]) i++;
else j++;
}
return res.toArray(new int[res.size()][]);
}A = [[0,2],[5,10],[13,23],[24,25]]
B = [[1,5],[8,12],[15,24],[25,26]]
i=0,j=0: max(0,1)=1, min(2,5)=2 β 1<=2 β [1,2]. A ends first β i=1
i=1,j=0: max(5,1)=5, min(10,5)=5 β 5<=5 β [5,5]. B ends first β j=1
i=1,j=1: max(5,8)=8, min(10,12)=10 β 8<=10 β [8,10]. A ends first β i=2
i=2,j=1: max(13,8)=13, min(23,12)=12 β 13>12 β no intersection. B ends β j=2
i=2,j=2: max(13,15)=15, min(23,24)=23 β 15<=23 β [15,23]. A ends β i=3
i=3,j=2: max(24,15)=24, min(25,24)=24 β 24<=24 β [24,24]. B ends β j=3
i=3,j=3: max(24,25)=25, min(25,26)=25 β 25<=25 β [25,25]. A ends β i=4
Output: [[1,2],[5,5],[8,10],[15,23],[24,24],[25,25]] β
GFG | Difficulty: Easy | Company: Amazon | Category: Sort + Adjacent Check
Given a list of intervals, check if any two intervals overlap.
Sort by start. After sorting, any overlap must be between adjacent intervals. Check if end of previous >= start of next.
| Step | Condition | Action |
|---|---|---|
| Sort | β | by start time |
| Check adjacent | prev.end >= curr.start |
return true |
| Update | β | prev.end = curr.end, advance |
class Solution {
static boolean isIntersect(int[][] intervals) {
Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
int e1 = intervals[0][1];
for (int i = 1; i < intervals.length; i++) {
if (e1 >= intervals[i][0]) {
return true;
}
e1 = intervals[i][1];
}
return false;
}
}public boolean isOverlapping(int[][] intervals) {
Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
for (int i = 1; i < intervals.length; i++) {
if (intervals[i][0] < intervals[i-1][1]) // strict < : touching not counted
return true;
}
return false;
}intervals = [[1,3],[2,5],[7,9]]
Sorted: [[1,3],[2,5],[7,9]]
e1=3
i=1: [2,5] β 3>=2 β return true β (overlap between [1,3] and [2,5])
LeetCode #253 | Difficulty: Hard | Company: Google, Facebook, Amazon | Category: Min-Heap
Given meeting intervals, find the minimum number of conference rooms required.
Sort by start. Use a min-heap of end times. For each new meeting: if the earliest-ending room is free (heap.peek() <= start), reuse it. Otherwise allocate a new room. Heap size = rooms in use.
| Step | Condition | Action |
|---|---|---|
| Sort | β | by start time |
| Free room exists | heap.peek() <= interval[0] |
heap.poll() β reuse room |
| No free room | heap empty or all busy | allocate new room |
| Always | β | heap.offer(interval[1]) β track end |
| Result | β | heap.size() |
class Solution {
public int minMeetingRooms(int[] start, int[] end) {
Arrays.sort(start);
Arrays.sort(end);
int i = 0, j = 0, room = 0, res = 0;
while (i < start.length) {
if (start[i] < end[j]) {
room++;
res = Math.max(room, res);
i++;
} else {
room--;
j++;
}
}
return res;
}
}Note: Two-pointer approach β sort
start[]andend[]separately. If a new meeting starts before the earliest-ending meeting ends β need a new room. Otherwise a room frees up. O(1) extra space vs O(n) for heap. Reference uses min-heap withint[][]input format.
public int minMeetingRooms(int[][] intervals) {
Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
PriorityQueue<Integer> heap = new PriorityQueue<>(); // min-heap of end times
for (int[] interval : intervals) {
if (!heap.isEmpty() && heap.peek() <= interval[0])
heap.poll(); // earliest room is free β reuse it
heap.offer(interval[1]); // assign room, track its end time
}
return heap.size();
}intervals = [[0,30],[5,10],[15,20]]
Sorted: [[0,30],[5,10],[15,20]]
[0,30]: heap empty β offer 30. heap=[30]
[5,10]: peek=30 > 5 β new room, offer 10. heap=[10,30]
[15,20]: peek=10 <= 15 β reuse! poll 10, offer 20. heap=[20,30]
heap.size() = 2 β
Educative | Difficulty: Hard | Company: Amazon, Google | Category: Min-Heap
Given jobs with start, end, and CPU load, find the maximum CPU load at any time.
Same structure as Min Meeting Rooms, but instead of counting rooms, accumulate CPU load. Subtract load of completed jobs (heap by end time), add current job's load, track maximum.
| Step | Condition | Action |
|---|---|---|
| Sort | β | by start time |
| Pop completed | heap.peek()[1] <= job[0] |
subtract load, pop |
| Add job | β | push [job[2], job[1]] (load, end), add load |
| Track | β | maxLoad = max(maxLoad, currentLoad) |
public int findMaxCPULoad(int[][] jobs) {
Arrays.sort(jobs, (a, b) -> a[0] - b[0]);
PriorityQueue<int[]> heap = new PriorityQueue<>((a, b) -> a[1] - b[1]); // min by end
int currentLoad = 0, maxLoad = 0;
for (int[] job : jobs) {
// Remove all jobs that ended before this one starts
while (!heap.isEmpty() && heap.peek()[1] <= job[0]) {
currentLoad -= heap.poll()[0]; // subtract their load
}
heap.offer(new int[]{job[2], job[1]}); // [load, endTime]
currentLoad += job[2];
maxLoad = Math.max(maxLoad, currentLoad);
}
return maxLoad;
}jobs = [[1,4,3],[2,5,4],[7,9,6]] (start, end, load)
Sorted: [[1,4,3],[2,5,4],[7,9,6]]
[1,4,3]: heap empty, add. heap=[[3,4]], load=3, max=3
[2,5,4]: peek.end=4 > 2 β no pop. add. heap=[[3,4],[4,5]], load=7, max=7
[7,9,6]: peek.end=4<=7 β pop [3,4], load=4
peek.end=5<=7 β pop [4,5], load=0
add [6,9]. heap=[[6,9]], load=6, max=7
Output: 7 β
LeetCode #621 | Difficulty: Medium | Company: Facebook, Amazon | Category: Greedy + Heap
Given tasks with a cooldown
n, find the minimum time to execute all tasks.
Always execute the most frequent remaining task. Use a max-heap. After each cooldown window of n+1 slots, push back tasks with decremented counts. Answer = total slots used.
public int leastInterval(char[] tasks, int n) {
int[] freq = new int[26];
for (char c : tasks) freq[c - 'A']++;
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder());
for (int f : freq) if (f > 0) maxHeap.offer(f);
int time = 0;
while (!maxHeap.isEmpty()) {
List<Integer> temp = new ArrayList<>();
int cycle = n + 1; // one full cooldown window
while (cycle > 0 && !maxHeap.isEmpty()) {
temp.add(maxHeap.poll() - 1); // execute task
cycle--;
}
for (int t : temp) if (t > 0) maxHeap.offer(t);
time += maxHeap.isEmpty() ? (n + 1 - cycle) : (n + 1); // idle or full cycle
}
return time;
}tasks = ['A','A','A','B','B','B'], n = 2
freq: A=3, B=3. maxHeap=[3,3]
Cycle 1: execute A(β2), B(β2). cycle=1 left β idle 1. time=3
Cycle 2: execute A(β1), B(β1). cycle=1 left β idle 1. time=6
Cycle 3: execute A(β0), B(β0). heap empty β time += 2. time=8
Output: 8 (A B idle A B idle A B) β
LeetCode #435 | Difficulty: Medium | Company: Google, Amazon | Category: Greedy Sort by End
Find the minimum number of intervals to remove to make the rest non-overlapping.
Greedy: sort by end time. Keep as many intervals as possible. When two overlap, always remove the one with the later end (keep the earlier end β leaves more room for future intervals). Count removals.
| Step | Condition | Action |
|---|---|---|
| Sort | β | by end time |
| No overlap | intervals[i][0] >= prevEnd |
keep it, prevEnd = intervals[i][1] |
| Overlap | intervals[i][0] < prevEnd |
remove (count++), keep prevEnd unchanged |
public int eraseOverlapIntervals(int[][] intervals) {
Arrays.sort(intervals, (a, b) -> a[1] - b[1]); // sort by END
int count = 0;
int prevEnd = intervals[0][1];
for (int i = 1; i < intervals.length; i++) {
if (intervals[i][0] < prevEnd) {
count++; // overlap β remove current (later end)
} else {
prevEnd = intervals[i][1]; // no overlap β keep, advance end
}
}
return count;
}intervals = [[1,2],[2,3],[3,4],[1,3]]
Sort by end: [[1,2],[2,3],[1,3],[3,4]]
prevEnd=2
i=1: [2,3] β 2>=2 β keep. prevEnd=3
i=2: [1,3] β 1<3 β overlap! count=1
i=3: [3,4] β 3>=3 β keep. prevEnd=4
Output: 1 (remove [1,3]) β
LeetCode #452 | Difficulty: Medium | Company: Microsoft, Amazon | Category: Greedy Sort by End
Balloons are represented as
[xstart, xend]. An arrow shot at x bursts all balloons withxstart <= x <= xend. Find minimum arrows needed.
Identical greedy to Non-overlapping. Sort by end. Shoot at the end of the first balloon. Any balloon whose start β€ current arrow position is burst. When a balloon's start > arrow position, shoot a new arrow at its end.
| Step | Condition | Action |
|---|---|---|
| Sort | β | by end (xend) |
| Init | β | arrows=1, arrowPos=points[0][1] |
| Not hit | points[i][0] > arrowPos |
arrows++, arrowPos = points[i][1] |
| Already hit | points[i][0] <= arrowPos |
skip (same arrow bursts it) |
public int findMinArrowShots(int[][] points) {
Arrays.sort(points, (a, b) -> Integer.compare(a[1], b[1])); // sort by end
int arrows = 1;
int arrowPos = points[0][1];
for (int i = 1; i < points.length; i++) {
if (points[i][0] > arrowPos) { // balloon not burst by current arrow
arrows++;
arrowPos = points[i][1]; // shoot new arrow at this balloon's end
}
}
return arrows;
}points = [[10,16],[2,8],[1,6],[7,12]]
Sort by end: [[1,6],[2,8],[7,12],[10,16]]
arrows=1, arrowPos=6
i=1: [2,8] β 2<=6 β burst by arrow at 6. skip.
i=2: [7,12] β 7>6 β new arrow! arrows=2, arrowPos=12
i=3: [10,16] β 10<=12 β burst by arrow at 12. skip.
Output: 2 β
LeetCode #1229 | Difficulty: Medium | Company: Facebook, Google | Category: Two-pointer on sorted
Given availability slots of two people and a meeting duration, find the earliest slot where both are free for the required duration.
Sort both slot lists. Use two pointers. For each pair of overlapping slots, check if the intersection is long enough. Advance the pointer with the earlier end.
public List<Integer> minAvailableDuration(int[][] slots1, int[][] slots2, int duration) {
Arrays.sort(slots1, (a, b) -> a[0] - b[0]);
Arrays.sort(slots2, (a, b) -> a[0] - b[0]);
int i = 0, j = 0;
while (i < slots1.length && j < slots2.length) {
int start = Math.max(slots1[i][0], slots2[j][0]);
int end = Math.min(slots1[i][1], slots2[j][1]);
if (end - start >= duration)
return Arrays.asList(start, start + duration);
if (slots1[i][1] <= slots2[j][1]) i++;
else j++;
}
return new ArrayList<>();
}slots1=[[10,50],[60,120],[140,210]], slots2=[[0,15],[60,70]], duration=8
Sort both (already sorted).
i=0,j=0: max(10,0)=10, min(50,15)=15 β 15-10=5 < 8. slots2 ends first β j=1
i=0,j=1: max(10,60)=60, min(50,70)=50 β 50<60 β no overlap. slots1 ends first β i=1
i=1,j=1: max(60,60)=60, min(120,70)=70 β 70-60=10 >= 8 β return [60, 68] β
LeetCode #759 | Difficulty: Hard | Company: Uber, Airbnb, Google | Category: Merge All + Gap Scan
Given schedules of multiple employees, find the list of finite intervals representing their common free time.
Flatten all intervals into one list, sort by start, merge overlapping. Then scan the merged list for gaps between consecutive intervals β those gaps are free time for everyone.
| Step | Action | Note |
|---|---|---|
| Flatten | collect all intervals | from all employees |
| Sort | by start | treat as one big list |
| Merge | standard merge | same as Problem 1 |
| Gaps | merged[i].end to merged[i+1].start |
free time intervals |
public List<Interval> employeeFreeTime(List<List<Interval>> schedule) {
// Flatten all intervals
List<int[]> all = new ArrayList<>();
for (List<Interval> emp : schedule)
for (Interval iv : emp)
all.add(new int[]{iv.start, iv.end});
all.sort((a, b) -> a[0] - b[0]);
// Merge overlapping
List<int[]> merged = new ArrayList<>();
int[] curr = all.get(0);
for (int[] iv : all) {
if (iv[0] <= curr[1]) curr[1] = Math.max(curr[1], iv[1]);
else { merged.add(curr); curr = iv; }
}
merged.add(curr);
// Find gaps
List<Interval> res = new ArrayList<>();
for (int i = 1; i < merged.size(); i++)
res.add(new Interval(merged.get(i-1)[1], merged.get(i)[0]));
return res;
}Employee 1: [[1,3],[6,7]]
Employee 2: [[2,4]]
Employee 3: [[2,5],[9,12]]
Flattened + sorted: [1,3],[2,4],[2,5],[6,7],[9,12]
Merged: [1,5],[6,7],[9,12]
Gaps: [5,6] and [7,9]
Output: [[5,6],[7,9]] β
LeetCode #1288 | Difficulty: Medium | Company: Google | Category: Sort + Greedy
Given a list of intervals, remove all intervals that are covered by another interval. Return the count of remaining intervals.
Sort by start ascending, then by end descending (so for same start, longer interval comes first). Walk through β if current interval's end β€ maxEnd seen so far, it's covered. Otherwise keep it.
| Step | Condition | Action |
|---|---|---|
| Sort | start asc, end desc | longer interval first for same start |
| Covered | interval[1] <= maxEnd |
skip (covered) |
| Not covered | interval[1] > maxEnd |
keep, count++, update maxEnd |
public int removeCoveredIntervals(int[][] intervals) {
// Sort by start asc; for same start, by end desc (longer first)
Arrays.sort(intervals, (a, b) -> a[0] != b[0] ? a[0] - b[0] : b[1] - a[1]);
int count = 0, maxEnd = 0;
for (int[] interval : intervals) {
if (interval[1] > maxEnd) {
count++;
maxEnd = interval[1];
}
// else: covered by a previous interval, skip
}
return count;
}intervals = [[1,4],[3,6],[2,8]]
Sort (start asc, end desc): [[1,8],[1,4] wait... β [[1,4],[1,8] β no]
Actually: [[1,4],[2,8],[3,6]]
Sort: start=1β[1,4]; start=2β[2,8]; start=3β[3,6]
maxEnd=0
[1,4]: 4>0 β count=1, maxEnd=4
[2,8]: 8>4 β count=2, maxEnd=8
[3,6]: 6<=8 β covered, skip
Output: 2 β
LeetCode #352 | Difficulty: Hard | Company: Google, Palantir | Category: TreeMap + Merge
A data stream generates integers. After each
addNum(val),getIntervals()returns the sorted disjoint intervals covering all numbers added.
Use a TreeMap<Integer, Integer> where key = start, value = end. For each new number, find if it can extend an existing interval or merge adjacent ones. floorKey and ceilingKey give the neighbours in O(log n).
class SummaryRanges {
TreeMap<Integer, Integer> map = new TreeMap<>();
public void addNum(int val) {
if (map.containsKey(val)) return;
Integer lo = map.floorKey(val); // interval starting at or before val
Integer hi = map.ceilingKey(val); // interval starting at or after val
boolean mergeLeft = lo != null && map.get(lo) + 1 >= val;
boolean mergeRight = hi != null && hi <= val + 1;
if (mergeLeft && mergeRight) {
map.put(lo, map.get(hi)); // merge both sides
map.remove(hi);
} else if (mergeLeft) {
map.put(lo, Math.max(map.get(lo), val));
} else if (mergeRight) {
map.put(val, map.get(hi));
map.remove(hi);
} else {
map.put(val, val); // new standalone interval
}
}
public int[][] getIntervals() {
int[][] res = new int[map.size()][2];
int i = 0;
for (Map.Entry<Integer, Integer> e : map.entrySet())
res[i++] = new int[]{e.getKey(), e.getValue()};
return res;
}
}addNum(1): map={1:1} β [[1,1]]
addNum(3): map={1:1,3:3} β [[1,1],[3,3]]
addNum(7): map={1:1,3:3,7:7} β [[1,1],[3,3],[7,7]]
addNum(2): lo=1(end=1), hi=3: mergeLeft(1+1>=2β) mergeRight(3<=3β)
β merge both: map={1:3,7:7} β [[1,3],[7,7]]
addNum(6): lo=1(end=3), hi=7: mergeRight(7<=7β)
β map.put(6,7), remove 7: map={1:3,6:7} β [[1,3],[6,7]]
| Problem | Category | Key technique |
|---|---|---|
| Merge Intervals | Sort + scan | sort start; newStart<=end β extend |
| Insert Interval | 3-phase | before / merge-overlap / after |
| Interval Intersections | Two-pointer | max(s1,s2)<=min(e1,e2); advance smaller end |
| Overlapping Check | Sort + adjacent | sorted end[i] >= start[i+1] |
| Min Meeting Rooms | Min-heap / two-ptr | heap of ends; peek <= newStart β reuse |
| Max CPU Load | Min-heap | same as rooms, track running load |
| Task Scheduler | Max-heap + greedy | most frequent first per cooldown window |
| Non-overlapping | Greedy sort end | keep earliest end; count removed |
| Min Arrows | Greedy sort end | shoot at curr.end; skip all covered |
| Meeting Scheduler | Two-pointer | sort both, intersect, check duration |
| Employee Free Time | Merge + gap scan | flatten, merge, find gaps |
| Remove Covered | Sort + maxEnd | sort startβ endβ; track maxEnd |
| Data Stream Intervals | TreeMap merge | floorKey/ceilingKey to find neighbours |