Median of a Stream of Integers
Keep the numbers in two heaps. low is a max-heap that holds the smaller half, and high is a min-heap that holds the larger half. Each new number is pushed into low, then low's largest moves to high, and if high became bigger, its smallest moves back. The median is then low.top(), or the average of both tops.
Every value in low is ≤ every value in high, and low has the same size as high or one more. With both true, the two tops sit right next to the middle of the sorted list.
Problem
Numbers arrive one at a time, like a live feed. After each new number, you need the median of all the numbers seen so far.
The median is the middle value after sorting. If the count is odd, it is the middle number. If the count is even, it is the average of the two middle numbers.
Given arr, return a list of doubles. The i-th answer is the median of arr[0] … arr[i]. Whole-number medians are still returned as doubles, such as 4.0.
An integer array arr. Think of it as a stream: element i arrives after elements 0 … i − 1.
A vector<double> with one entry per input number. Entry i is the median of the first i + 1 numbers.
After 5 the median is 5. After 15, the two middle values are 5 and 15, so the median is 10. After 1, the sorted list [1, 5, 15] has middle 5. After 3, the list [1, 3, 5, 15] has middle pair 3 and 5, so the median is 4. After 2, the list [1, 2, 3, 5, 15] has middle 3. After 8, the list [1, 2, 3, 5, 8, 15] has middle pair 3 and 5, so the median is 4.
Every prefix contains only 2s, so every median is 2.
- • Each value fits in a 32-bit int (the function takes a `vector<int>`).
First, think yourself
Before opening any solution, try to answer these on paper. Use hints only when stuck — each hint is counted.
- 1.
When one new number arrives, does the median move a lot, or only a little? What part of the old answer can I reuse?
- 2.
To find the median of 5 numbers, do I need all 5 in sorted order, or only the one or two in the middle?
- 3.
If I kept the numbers in sorted order, where would a new number go, and how many numbers would have to move?
- 4.
Split the sorted numbers into a lower half and an upper half. Which two numbers sit on the boundary between them?
- 5.
Which data structure can hand me its largest value fast, and which one can hand me its smallest value fast?
Brute force approach
Better approach
Optimized approach
Why does this algorithm work?
Dry run
All approaches compared
| Approach | Idea | Time | Space |
|---|---|---|---|
| Brute Force | After each number, copy the numbers so far, sort the copy, and read the middle | O(N² log N) | O(N) |
| Better | Find each number's slot with binary search and insert it; the median is read from the middle index | O(N²) | O(N) |
| Optimal | Lower half in a max-heap, upper half in a min-heap; the two tops sit next to the median | O(N log N) | O(N) |
Why does this belong to this pattern?
- “median of a stream”The middle of the sorted numbers, but we never need them all in order. Only the boundary between the two halves matters.
- “after each new number”We must answer at every step, so each step has to be fast. Re-sorting from scratch is too slow.
- “running”The answer changes a little each step. Keep both sides ready so each change is a heap move.
The median only needs the two numbers next to the middle, so keep those two tops ready at all times. The rule for this problem is the size rule plus the order rule: every low value ≤ every high value, and low is the same size as high or one bigger. Find Median from Data Stream uses the same rule, but splits the work between addNum and findMedian. Sliding Window Median also removes numbers, which needs lazy deletion or a multiset on top of the heaps.
Don't make this mistake
This looks like the two-heap idea, but it never moves a value across unless the sizes are off. Dry run on [1, 6, 100, 3, 50, 2]: after 1 and 6 the code is right (median 3.5). After 100, low is {1, 100} and high is {6}. The sizes differ by 1 with low bigger, so nothing moves, and the code returns 100. The right median is 6. The 100 is stuck in low, above high's 6, which breaks the order rule. Step 5 fails the same way: after 50 it returns 50 instead of 6.
A plain priority_queue<int> returns its largest value first. Dry run on [1, 6, 100, 3, 50, 2]: after 100 the code moves 100 into high. Then the size step takes high's top, which is now 100, and moves it back to low. low is {1, 100} and the answer is 100 instead of 6. After 3 the answer is 51.5 instead of 4.5. The top of the upper half must be its smallest.
Both tops are ints, so C++ divides them as ints and drops the fraction. Dry run on [1, 6, 100, 3, 50, 2]: after 6 the middle pair is 1 and 6, and (1 + 6) / 2 gives 3, but the median is 3.5. After 3, the code gives 4 instead of 4.5. Cast to double before the division.
Want to implement this?
Write it yourself on GeeksforGeeks, then come back and compare your code with the solutions above.
Open coding workspaceHow this concept connects to System Design & C++ Systems
Top engineering interviews test whether you understand how algorithms like Two Heaps translate into production architectures and low-level memory mechanics.