Find Median from Data Stream

hardTests passed 2026-11-11v1.0.0

1. Core Invariant / The Trick

Maintain two balanced heaps: `lo` (max-heap of lower half) and `hi` (min-heap of upper half). Preserve two invariants on every `addNum`: (1) `max(lo) <= min(hi)`, and (2) `len(lo) >= len(hi)` differing by at most 1. Cleanest 3-step push: push into `lo`, pop `max(lo)` to `hi`, and if `len(hi) > len(lo)` pop `min(hi)` back to `lo`.

2. Complexity Bounds & Proof

Three-step push guarantees ordering and size invariants without conditional branches on comparison; `findMedian` is O(1).

3. Minimal Idiomatic Code

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

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

    def findMedian(self) -> float:
        if len(self.lo) > len(self.hi):
            return float(-self.lo[0])
        return (-self.lo[0] + self.hi[0]) / 2.0

4. Failure Modes & Interview Follow-ups

Pitfalls:- Comparing new value directly against heap tops creates fragile branching. - Integer division `(lo[0] + hi[0]) / 2` in C++/Java without `.0` / `2.0` truncates `.5`.
Follow-ups:- If all numbers are in `[0, 100]`, use a 101-bucket counting array for O(1) `findMedian`.

Personal Notes & Scratchpad (Local-First Yjs CRDT)

Saved instantaneously in IndexedDB. Streams live across peers via Hocuspocus when signed in.

Related Invariants

Browser Scratchpad
Console Output
Click Run or press Cmd/Ctrl+Enter to execute locally in a Web Worker.