Median of a Data Stream
AmazonSoftware Engineer · OnsiteLast updated
Design a data structure that continuously receives a stream of integers and efficiently supports retrieving the current running median, handling potential integer overflow during the median calculation.
Problem Description
Design a data structure that accepts a dynamic stream of integers and supports finding the median of all received numbers at any time.
The median is the middle value in an ordered integer list:
- If the size of the list is odd, the median is the middle element.
- If the size of the list is even, the median is the arithmetic mean of the two middle elements.
Implement the MedianFinder class:
class MedianFinder:
def __init__(self):
"""Initialize the data structure."""
pass
def add_num(self, num: int) -> None:
"""Inserts an integer into the data stream."""
pass
def find_median(self) -> float:
"""Returns the median of all elements so far."""
pass
Note on arithmetic safety: In typed systems or environments with fixed-width integers, adding two large middle numbers can cause integer overflow. Ensure your logic prevents overflow when calculating the mean.
Examples
Example 1
mf = MedianFinder()
mf.add_num(1)
mf.add_num(2)
print(mf.find_median()) # Output: 1.5
mf.add_num(3)
print(mf.find_median()) # Output: 2.0
Explanation:
- Stream:
[1], median is1.0. - Stream:
[1, 2], median is(1 + 2) / 2.0 = 1.5. - Stream:
[1, 2, 3], median is2.0.
Example 2 (Large Values)
mf = MedianFinder()
mf.add_num(2147483647)
mf.add_num(2147483647)
print(mf.find_median()) # Output: 2147483647.0
Constraints
-2^31 <= num <= 2^31 - 1- Up to
100,000total calls will be made toadd_numandfind_median. find_median()is guaranteed to be called only when at least one number has been added.- Expected time complexity:
O(log n)foradd_num,O(1)forfind_median.
Edge Cases to Consider
- Adding duplicates or negative numbers.
- Stream with both values near
2^31 - 1or near-2^31wherea + bwould overflow a signed 32-bit integer. - Querying after single element insertions.
Hints
Hint 1
Consider dividing the stream into two halves: a lower half and an upper half.
Hint 2
Use two heaps: a max-heap for the lower half and a min-heap for the upper half, keeping their sizes balanced within a difference of one.
Hint 3
When computing the average of two numbers `a` and `b`, avoid naive `(a + b) / 2` by using `a + (b - a) / 2.0` or floating-point division.
Follow-up questions
- If 99% of all integer inputs are in the range [0, 100], how could you optimize the time and memory complexity?
- How would you extend this design to compute the median over a sliding window of the last K elements?
- How would you handle this problem in a distributed system where data is partitioned across multiple worker nodes?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.