Data Stream as Disjoint Intervals
Master this topic with zero to advance depth.
Data Stream as Disjoint Intervals
Given a data stream input of non-negative integers a1, a2, ..., an, summarize the numbers seen so far as a list of disjoint intervals.
Implement the SummaryRanges class:
SummaryRanges()Initializes the object with an empty stream.void addNum(int value)Adds the integervalueto the stream.int[][] getIntervals()Returns a summary of the integers in the stream currently as a list of disjoint intervals[start_i, end_i]. The answer should be sorted bystart_i.
Visual Representation
addNum(1): [1,1]
addNum(3): [1,1], [3,3]
addNum(7): [1,1], [3,3], [7,7]
addNum(2): [1,3], [7,7] <-- 2 merges [1,1] and [3,3]
addNum(6): [1,3], [6,7] <-- 6 merges with [7,7]
Key: When adding N, find and merge with adjacent intervals.
[L, N-1] + N + [N+1, R] => [L, R]Examples
Level I: Brute Force - Rebuild on Every getIntervals
Intuition
Simply store all seen numbers in a sorted list. On every getIntervals() call, rebuild the disjoint intervals from scratch by scanning through the sorted list and merging consecutive numbers.
Detailed Dry Run
addNum(1), addNum(3), addNum(7), addNum(2) nums = {1, 2, 3, 7} getIntervals(): sorted = [1, 2, 3, 7] Scan: 1->next is 2 (consecutive), 2->next is 3 (consecutive), 3->next is 7 (gap) Intervals: [1,3], [7,7]
Level II: Maintenance of Sorted Intervals
Intuition
Instead of rebuilding the intervals every time, we can maintain a sorted list of disjoint intervals. When adding a new number, we use binary search to find where it would fit among existing intervals, then check if it can be merged with its left or right neighbors. This significantly speeds up getIntervals at the cost of for addNum due to array shifts.
Detailed Dry Run
addNum(1): [[1,1]] addNum(3): [[1,1], [3,3]] addNum(2): 2 is between [1,1] and [3,3]. Merges both -> [[1,3]] addNum(7): [[1,3], [7,7]]
Level III: TreeMap / Sorted Map for O(log N) addNum
Intuition
Instead of re-sorting each time, maintain intervals in a sorted map (TreeMap in Java, SortedDict in Python, etc.) keyed by start. When adding a new number val:
- Find the interval that might start right after
val+1(right neighbor). - Find the interval whose start <=
val(left neighbor), and check if it ends atval-1. - Based on overlapping/adjacency with left and right neighbors, merge intervals accordingly.
This gives O(log N) per addNum and O(N) per getIntervals.
Detailed Dry Run
addNum(1): map = {1:[1,1]} addNum(3): map = {1:[1,1], 3:[3,3]} addNum(7): map = {1:[1,1], 3:[3,3], 7:[7,7]} addNum(2): val=2. Right neighbor start=3 (3 == 2+1). Left neighbor: key=1, [1,1]. end=1 == 2-1. Merge both: [min(1,2)=1, max(1,3)=3] = [1,3]. Remove key 3. map = {1:[1,3], 7:[7,7]} addNum(6): val=6. Right neighbor start=7 (7 == 6+1). Check left: key=1, [1,3]. end=3 != 5. Only merge with right: [6, 7]. Remove key 7. map = {1:[1,3], 6:[6,7]} getIntervals: [[1,3],[6,7]]
Found an issue or have a suggestion?
Help us improve! Report bugs or suggest new features on our Telegram group.