Find Median from Data Stream
Picture
After 2, median is 2. After 2,8 it is 5. After 2,5,8 it is 5.
Predict
What invariant relates every lower-half value to every upper-half value?
Walk through the small example
Lower values are no larger. The median is the boundary between a lower max-heap and upper min-heap.
Mental model and complete Python solution
Think of two bowls touching at their rims. The left bowl reveals its largest value; the right reveals its smallest. Rebalance so the middle always sits at those rims.
What the function promises: MedianFinder accepts numeric values. median raises ValueError before any insertion and returns a float.
from heapq import heappush, heappop
class MedianFinder:
def __init__(self):
self.lower, self.upper = [], []
def add(self, value):
heappush(self.lower, -value)
heappush(self.upper, -heappop(self.lower))
if len(self.upper) > len(self.lower):
heappush(self.lower, -heappop(self.upper))
def median(self):
if not self.lower:
raise ValueError("No values")
if len(self.lower) > len(self.upper):
return float(-self.lower[0])
return (-self.lower[0] + self.upper[0]) / 2
stream = MedianFinder()
stream.add(2); assert stream.median() == 2
stream.add(8); assert stream.median() == 5
stream.add(5); assert stream.median() == 5
Work and storage: Insertion O(log n), median O(1), storage O(n).
When to recognize it: The median is the boundary between a lower max-heap and upper min-heap.
Check before applying it: Size balance alone is insufficient; cross-half ordering must also hold.
Change one rule
Old values expire from a sliding window.
See what changes
Add lazy deletion maps or balanced ordered multisets; heaps cannot remove arbitrary expired items directly.
Connect
Heap: keep the next extreme ready: Each heap exposes the one boundary value we need.
invariants: Maintain both ordering and size promises after every insertion.