Skip to content
Reliable Data Engineering
Practice problem medium heapstreamingpercentiles
Solve it in the browser (Python editor)

Running Median of a Latency Stream

Difficulty: Medium · Topics: heap, streaming, percentiles · Asked at: Google, Datadog, Amazon, Citadel

Problem

Implement RunningMedian with add(x) and median() -> float. For an even count return the mean of the two middle values. median() on an empty structure raises ValueError.

Starter code

class RunningMedian:
    def __init__(self):
        pass

    def add(self, x: float) -> None:
        pass

    def median(self) -> float:
        pass

Hints

Hint 1

A max-heap for the lower half (store negatives) and a min-heap for the upper half; keep sizes balanced within 1.

Solution

import heapq

class RunningMedian:
    def __init__(self):
        self.lo = []   # max-heap via negatives
        self.hi = []   # min-heap

    def add(self, x: float) -> None:
        heapq.heappush(self.lo, -x)
        heapq.heappush(self.hi, -heapq.heappop(self.lo))     # move largest of lo to hi
        if len(self.hi) > len(self.lo):
            heapq.heappush(self.lo, -heapq.heappop(self.hi))

    def median(self) -> float:
        if not self.lo:
            raise ValueError("empty")
        if len(self.lo) > len(self.hi):
            return float(-self.lo[0])
        return (-self.lo[0] + self.hi[0]) / 2

Tests

Your solution should pass these:

rm = RunningMedian()
out = []
for x in [5, 15, 1, 3, 8, 7, 9, 10]:
    rm.add(x)
    out.append(rm.median())
assert out == [5.0, 10.0, 5.0, 4.0, 5.0, 6.0, 7.0, 7.5]
try:
    RunningMedian().median()
    assert False
except ValueError:
    pass

Explanation

O(log n) per add, O(1) per median, O(n) memory. That memory is the catch: for p95/p99 over billions of values in monitoring you use approximate sketches (t-digest, KLL, DDSketch) with bounded memory and mergeability across hosts and time windows.