Data Stream as Disjoint Intervals
Problem (restated)
Stream of integers. After each addNum(val), the structure should expose getIntervals(): the disjoint closed intervals covering every added value, sorted, merged whenever two intervals touch or overlap.
Intuition
Keep the current disjoint list sorted. A new point is a degenerate interval [val, val]. Sweep the existing intervals: those strictly left stay, those that overlap or touch (b >= val-1 and a <= val+1) merge into it, those strictly right stay after it.
Approaches
Insert and merge
UnverifiedIdea. One pass over the sorted list. b < val-1 → copy. a > val+1 → emit the new interval once, then copy. Else expand [val,val] to cover [a,b]. Append at the end if it was the rightmost.
Walkthrough. 1, 3, 7 → [[1,1],[3,3],[7,7]]. Add 2 → [1,3] merges. Add 6 → [6,7].
Trade-offs. Linear scan is fine while the unique-count is small. A balanced tree of intervals is O(log n) insert; interviews usually accept the scan. Touching counts as merge ([1,1] and [2,2] become [1,2]).
export class SummaryRanges {
private ivs: number[][] = [];
addNum(val: number): void {
const n = [val, val];
const res: number[][] = [];
let placed = false;
for (const iv of this.ivs) {
const a = iv[0]!, b = iv[1]!;
if (b < n[0]! - 1) res.push([a, b]);
else if (a > n[1]! + 1) {
if (!placed) { res.push(n); placed = true; }
res.push([a, b]);
} else {
n[0] = Math.min(n[0]!, a);
n[1] = Math.max(n[1]!, b);
}
}
if (!placed) res.push(n);
this.ivs = res;
}
getIntervals(): number[][] {
return this.ivs;
}
}
export class SummaryRanges {
private ivs: number[][] = [];
addNum(val: number): void {
const n = [val, val];
const res: number[][] = [];
let placed = false;
for (const iv of this.ivs) {
const a = iv[0]!, b = iv[1]!;
if (b < n[0]! - 1) res.push([a, b]);
else if (a > n[1]! + 1) {
if (!placed) { res.push(n); placed = true; }
res.push([a, b]);
} else {
n[0] = Math.min(n[0]!, a);
n[1] = Math.max(n[1]!, b);
}
}
if (!placed) res.push(n);
this.ivs = res;
}
getIntervals(): number[][] {
return this.ivs;
}
}
Template connection
Data-stream shape of Sweep Line: each insert is an event that is merged into the active disjoint set. Same merge rule as interval merge (LC 56), online.
Reflection
- The new point is
[val, val]. An old interval withb < val - 1stays. An old interval witha > val + 1means the point belongs between them. - Otherwise they touch or overlap: replace with
[min(a, val), max(b, val)]. One point can swallow two neighbors. - Inserting the same value twice does not grow the interval. An empty stream produces the single point.