AlgoMaster Logo

Find Median from Data Stream

hardFrequency8 min readUpdated July 10, 2026

Understanding the Problem

Finding a median in a static, sorted array is direct: take the middle element, or average the two middle elements when the length is even. Here numbers arrive one at a time as a stream, and after any insertion the current median may be requested. The challenge is maintaining a structure that supports both efficient insertion and fast median retrieval.

One approach keeps a sorted list and inserts each number into its correct position. That gives O(1) median access but O(n) insertion because of shifting. Another appends and sorts on every findMedian call, which is O(n log n) per query. Neither scales well with up to 50,000 operations.

We don't need the entire list sorted. We only need the one or two elements at the middle. If we split the data into a lower half and an upper half and can access the largest element of the lower half and the smallest element of the upper half, we have the median.

Key Constraints:

  • -10^5 <= num <= 10^5 → Values are bounded integers in a small range. The general solution needs a comparison-based structure, but the bounded range allows a counting-based approach (covered in Approach 3 and the follow-up).
  • At most 5 * 10^4 calls → With up to 50,000 operations, O(n) work per operation reaches 2.5 billion steps in the worst case, which is too slow. O(log n) per addNum keeps the total near 50,000 * 16, well within limits.
  • At least one element before findMedian → The empty case for median queries does not need handling.

Approach 1: Sort on Each Query

Intuition

Keep all numbers in a list, sort it whenever a median is requested, and take the middle. The median of an ordered list is defined by position, so sorting and indexing gives a correct answer with no extra bookkeeping during insertion.

Algorithm

  1. Maintain a list of all numbers added so far.
  2. When addNum(num) is called, append num to the list.
  3. When findMedian() is called, sort the list.
  4. If the list has odd length, return the middle element.
  5. If the list has even length, return the average of the two middle elements.

Example Walkthrough

1Initialize: data = [], no elements yet
[]
1/6

Code

Sorting from scratch on every query repeats work. The next approach maintains a structure that stays partially ordered during insertion, so the middle elements are always available in O(1).

Approach 2: Two Heaps (Max-Heap + Min-Heap)

Intuition

The median depends only on the element(s) at the boundary between the lower half and the upper half of the data. Two heaps maintain exactly that boundary without keeping everything sorted.

Split the numbers into two groups: a lower half and an upper half. Store the lower half in a max-heap, so its root is the largest small number, and store the upper half in a min-heap, so its root is the smallest large number. The median sits at the boundary between these two roots.

Keep the two heaps balanced in size, letting the max-heap (lower half) hold at most one more element than the min-heap. When the total count is odd, the median is the top of the max-heap. When the total count is even, the median is the average of both heap tops.

Algorithm

  1. Create two heaps: maxHeap (lower half) and minHeap (upper half).
  2. For addNum(num):
    • Add num to maxHeap.
    • Move the top of maxHeap to minHeap (this ensures the largest element from the lower half goes to the upper half if needed).
    • If minHeap has more elements than maxHeap, move the top of minHeap back to maxHeap.
  3. For findMedian():
    • If the heaps are equal size, return the average of both tops.
    • Otherwise, return the top of maxHeap (since it holds one extra element).

Example Walkthrough

maxHeap (lower half)
1Initialize: maxHeap = [], minHeap = []
[]
minHeap (upper half)
1Initialize: minHeap = []
[]
1/6

Code

Transition to the follow-up: the heap solution treats every value as distinct and unbounded. The constraints fix every value in [-10^5, 10^5], and the follow-up narrows the common case to [0, 100]. When values come from a small fixed range, counting how many times each value appears replaces the heaps entirely.

Approach 3: Bucket Counts (Bounded Value Range)

Intuition

When values are confined to a small range, the data is fully described by a count of how many times each value has appeared. With values in [0, 100], an array counts of 101 entries records the whole stream: counts[v] is how many times v was added. addNum increments one entry in O(1), independent of how many numbers came before.

The median is found by position. If the total count is n, the median element(s) sit at rank (n - 1) / 2 for odd n, or at ranks n/2 - 1 and n/2 for even n. Walking the count array from the smallest value and accumulating counts locates the value at any target rank, because the array is ordered by value.

Algorithm

  1. Keep an array counts sized to the value range and a running total.
  2. For addNum(num): increment counts[num] and total.
  3. For findMedian():
    • If total is odd, scan from the smallest value, accumulating counts until the running sum exceeds rank total / 2. Return that value.
    • If total is even, find the values at ranks total / 2 - 1 and total / 2 in one left-to-right scan and return their average.

The scan to find a value at a given rank moves through the count array. The range is fixed, so this scan is bounded by the range size rather than n.

Example Walkthrough

1Initialize: counts all 0, total = 0
000000
1/6

Code

The implementations below cover the full constraint range [-10^5, 10^5] by offsetting each value by 100000, giving an array of 200001 buckets. For the [0, 100] follow-up, the same code shrinks to 101 buckets with no offset.

This approach answers both follow-ups. When all values fall in [0, 100], R is small and a median query is fast. When 99% of values fall in [0, 100] and the rest are outliers, a hybrid keeps a count array for the common range and a separate structure (such as the two heaps from Approach 2) for the rare out-of-range values, combining the ranks from both when locating the median.