◀ THE GRIND — HEAP / PRIORITY QUEUE

Find Median From Data Stream

The drill: A running data stream needs its median available after every insert — sometimes the exact middle, sometimes the average of the two center values, always without a full re-sort.

THE BRIEFING — THE FULL DRILL, IN MY OWN WORDS

Numbers arrive one at a time into a running stream, and after every insert, the structure must be able to report the median of every value seen so far.

When the stream holds an odd count of numbers, the median is the single middle value once sorted; with an even count, it's the average of the two center values.

Insertion and the median query are separate operations that can be called in any interleaving, and each median request has to reflect exactly the numbers inserted up to that point.

EX 01
MedianFinder()
addNum(5)
addNum(3)
findMedian() → 4
EVEN COUNT, AVERAGE OF THE TWO MIDDLES
EX 02
MedianFinder()
addNum(1)
findMedian() → 1
addNum(2)
findMedian() → 1.5
addNum(3)
findMedian() → 2
MEDIAN RE-CHECKED AFTER EVERY INSERT
EX 03
MedianFinder()
addNum(-5)
addNum(-1)
addNum(-3)
findMedian() → -3
ALL NEGATIVE
THE HINTS — TAKE ONLY WHAT YOU NEED
HINT 1 THE NUDGE

Re-sorting the whole stream on every median query is honest but wasteful — most of that sorted order is thrown away a moment later. What's the only part of the order you actually need to keep straight?

HINT 2 THE STRUCTURE

Split the numbers into a lower half and an upper half, kept the same size (or off by one). The median only ever touches the boundary between those two halves.

HINT 3 ONE STEP FROM THE ANSWER

A max-heap holds the lower half, a min-heap holds the upper half. Insert into one, shuffle its top across to the other to keep the sizes balanced — the median is then just the top(s) of the heaps.

COACH'S BOARD — THE PATTERN, STEP BY STEP
THE BALANCED-HEAP READOUTPATTERN · TWO HEAPS, BALANCEDnew · add 1 · median? · add 2 · median? · add 3 · median?
new
add 1
median?
add 2
median?
add 3
median?
LOWER HALF (MAX-HEAP) · UPPER HALF (MIN-HEAP)
lower[]
upper[]
STEP 1

A fresh MedianFinder: two empty heaps, a max-heap for the lower half and a min-heap for the upper half.

STEP 1 / 8 · ← → WORK TOO
THE SPLITS — TWO PACES, TWO LANGUAGES
grind/find-median-from-data-stream.pyRACE PACE
LANG ▸
PACE ▸
class MedianFinder:
    def __init__(self):
        self.small = []  # max-heap (values negated) — the lower half
        self.large = []  # min-heap — the upper half

    def addNum(self, num: int) -> None:
        heapq.heappush(self.small, -num)
        heapq.heappush(self.large, -heapq.heappop(self.small))
        if len(self.large) > len(self.small):
            heapq.heappush(self.small, -heapq.heappop(self.large))

    def findMedian(self) -> float:
        if len(self.small) > len(self.large):
            return float(-self.small[0])
        return (-self.small[0] + self.large[0]) / 2.0
TIME O(LOG N) ADDNUM, O(1) FINDMEDIANSPACE O(N)PYTHON · RACE PACE · 15 LN

✓ CHIP-TIMED — ALL 4 SOLUTIONS RAN GREEN AGAINST SELF-AUTHORED CASES IN CI · JDK 21 · CPYTHON 3.12 · NOTHING PUBLISHES RED