Browse curriculum

Find Median from Data Stream

Keep a running median of a stream with two heaps: a max-heap for the smaller half and a min-heap for the larger half.

Problem Understanding

Find Median from Data Stream: design a MedianFinder with addNum(num), which adds a number from a stream, and findMedian(), which returns the median of all numbers added so far — the middle value, or the average of the two middle values when the count is even.

Example: addNum(1), addNum(2), findMedian() → 1.5; addNum(3), findMedian() → 2.

Attempt 1: Keep a Sorted List

Store the numbers in a sorted array and insert each new one in place; the median is then the middle element. findMedian is O(1), but every insertion shifts elements: O(n) per addNum. Re-sorting on every query is worse, O(n log n). The median only ever depends on the two middle values, so the rest of the order is wasted work.

The Intuition: Two Halves, Tops Facing Each Other

Split the numbers into a lower half and an upper half. The median only needs the largest of the lower half and the smallest of the upper half — exactly what a max-heap and a min-heap expose in O(1).

  1. low (max-heap) holds the smaller half; high (min-heap) the larger half.
  2. addNum: push onto low, then move low's largest to high — so everything in low stays ≤ everything in high.
  3. If high now has more elements than low, move high's smallest back. low keeps the extra element when the count is odd.
  4. findMedian: low's top if low is bigger, else the average of both tops.

Interactive Walkthrough

addNum EACH VALUE, THEN findMedian

TWO EMPTY HEAPS

stream

1

0

2

1

3

2

low (max-heap) · top →

empty

|

← top · high (min-heap)

empty

low size

0

high size

0

Median

—

 

low is a max-heap for the smaller half, high a min-heap for the larger half. Both start empty

Next

addNum(1): push 1 onto low

Stream

The stream runs along the top. Below it, low and high are drawn back to back with their tops at the seam, so the median always sits in the middle. Each add shows the push, the crossing to high, and any rebalance; then the median is read from the top or tops.

The approach, step by step

  1. Push, then cross over

    Push the number onto the max-heap low, then move low's largest value to the min-heap high.

  2. Rebalance

    If high now has more elements than low, move high's smallest value back to low.

  3. Read the median

    If low is larger, the median is low's top; otherwise it is the average of both tops.

Frequently asked questions

How do two heaps find the median of a stream?

Keep the smaller half of the numbers in a max-heap and the larger half in a min-heap, with sizes equal or the max-heap one larger. The median is the max-heap's top when the count is odd, or the average of both tops when it is even.


What is the time complexity of addNum and findMedian?

addNum does a constant number of heap pushes and pops, each O(log n), so it is O(log n). findMedian only reads the heap tops, so it is O(1). The heaps hold every number: O(n) space.


Why push to low and then move its largest to high?

It keeps the invariant that everything in low is no larger than everything in high without comparing against both tops by hand. A final check moves one value back from high when high gets bigger, so the sizes stay balanced.