before
phase
Scan sorted, non-overlapping intervals in three phases: pass the ones that end before the new interval starts, absorb every overlapping interval by expanding the new one, then pass the rest — all in a single O(n) sweep.
You have a sorted list of non-overlapping intervals and one new interval to insert. Return the merged list (still sorted, still non-overlapping). For example, [[1,3],[6,9]] with [2,5] inserted gives [[1,5],[6,9]] — the new interval overlaps [1,3], so they merge into [1,5], and [6,9] is untouched.
i = 0. The new interval tracks its current [start, end].intervals[i][1] < newInterval[0] (ends strictly before the new one starts), push unchanged and advance i.intervals[i][0] <= newInterval[1] (starts at or before the new one ends), expand: newInterval[0] = min(...), newInterval[1] = max(...). After the loop, push the expanded new interval.intervals[i][0] <= newInterval[1] — note the <=. Two intervals [1,3] and [3,5] touch at 3 and must merge. Using strict < leaves them separate and produces invalid output.The output array is O(n) in the worst case (no merges). There is no in-place version because the list may shrink (merges reduce count) or effectively grow (new interval between existing ones).
2-5 into [1-3, 6-9]. Three phases: skip intervals ending before new starts, merge overlapping ones, append the rest.function insert(
intervals: number[][],
newInterval: number[]
): number[][] {
const result: number[][] = [];
let i = 0;
const n = intervals.length;
// Phase 1: add all intervals that end before newInterval starts
while (i < n && intervals[i][1] < newInterval[0]) {
result.push(intervals[i]);
i++;
}
// Phase 2: merge all overlapping intervals into newInterval
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++;
}
result.push(newInterval); // insert the (possibly expanded) interval
// Phase 3: append remaining intervals unchanged
while (i < n) {
result.push(intervals[i]);
i++;
}
return result;
}result. This condition, intervals[i][1] < newInterval[0], is why these intervals are safe to copy without touching newInterval.<= newInterval[1]. Each overlap forces an expansion: min on the left end, max on the right. After the loop the new interval has absorbed every overlapper, and is pushed once.Merge Intervals (LC 56).[[1,2],[3,5],[6,7],[8,10]] with [4,8]" Phase 1 copies [1,2]; Phase 2 merges [3,5], [6,7], [8,10] into [3,10]; result is [[1,2],[3,10]].| "insert into sorted non-overlapping intervals" | three-phase single pass |
| overlap check for sorted intervals | start ≤ other end (and vice versa) |
| "merge intervals" after sorting | same three-phase scan |
| expand a range greedily | min(starts), max(ends) inside the loop |
function insert(intervals: number[][], newInterval: number[]): number[][] {
const result: number[][] = [];
let i = 0;
const n = intervals.length;
// Phase 1: before — ends before new starts
while (i < n && intervals[i][1] < newInterval[0]) result.push(intervals[i++]);
// Phase 2: overlap — starts before new ends → expand
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++;
}
result.push(newInterval);
// Phase 3: after — whatever remains
while (i < n) result.push(intervals[i++]);
return result;
}[a,b] overlaps with newInterval = [s,e] when scanning left to right (so a <= salready)?intervals = [[1,3],[6,9]], newInterval = [2,5]. What is returned?intervals[i][1] < newInterval[0](strict <)?result.push(newInterval) appears outside any loop. Why?intervals = [[1,5]], newInterval = [0,6]. What is returned?