Skip to content

Commit b29283c

Browse files
committed
Add median in a stream using heap-based approach
1 parent 2c15b8c commit b29283c

File tree

1 file changed

+98
-0
lines changed

1 file changed

+98
-0
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import heapq
2+
from typing import List
3+
4+
5+
def signum(a: int, b: int) -> int:
6+
"""
7+
Compare two integers.
8+
9+
Returns:
10+
1 if a > b
11+
-1 if a < b
12+
0 if a == b
13+
"""
14+
if a > b:
15+
return 1
16+
if a < b:
17+
return -1
18+
return 0
19+
20+
21+
def call_median(
22+
element: int,
23+
max_heap: List[int],
24+
min_heap: List[int],
25+
median: int,
26+
) -> int:
27+
"""
28+
Insert an element into heaps and update median.
29+
"""
30+
case = signum(len(max_heap), len(min_heap))
31+
32+
# Case 0: both heaps have same size
33+
if case == 0:
34+
if element > median:
35+
heapq.heappush(min_heap, element)
36+
median = min_heap[0]
37+
else:
38+
heapq.heappush(max_heap, -element)
39+
median = -max_heap[0]
40+
41+
# Case 1: max heap has more elements
42+
elif case == 1:
43+
if element > median:
44+
heapq.heappush(min_heap, element)
45+
else:
46+
heapq.heappush(min_heap, -heapq.heappop(max_heap))
47+
heapq.heappush(max_heap, -element)
48+
median = (-max_heap[0] + min_heap[0]) // 2
49+
50+
# Case -1: min heap has more elements
51+
else:
52+
if element > median:
53+
heapq.heappush(max_heap, -heapq.heappop(min_heap))
54+
heapq.heappush(min_heap, element)
55+
else:
56+
heapq.heappush(max_heap, -element)
57+
median = (-max_heap[0] + min_heap[0]) // 2
58+
59+
return median
60+
61+
62+
def median_in_a_stream(numbers: List[int]) -> List[int]:
63+
"""
64+
Find the median after each insertion in a stream of integers.
65+
66+
Uses two heaps and follows the classic running median logic.
67+
68+
Args:
69+
numbers: List of integers
70+
71+
Returns:
72+
List of medians after each insertion
73+
74+
Raises:
75+
ValueError: If the input list is empty
76+
77+
>>> median_in_a_stream([20, 14, 13, 16, 17])
78+
[20, 17, 14, 15, 16]
79+
>>> median_in_a_stream([5, 15, 1, 3])
80+
[5, 10, 5, 4]
81+
>>> median_in_a_stream([])
82+
Traceback (most recent call last):
83+
...
84+
ValueError: Input list must not be empty
85+
"""
86+
if not numbers:
87+
raise ValueError("Input list must not be empty")
88+
89+
max_heap: List[int] = []
90+
min_heap: List[int] = []
91+
median = 0
92+
result: List[int] = []
93+
94+
for element in numbers:
95+
median = call_median(element, max_heap, min_heap, median)
96+
result.append(median)
97+
98+
return result

0 commit comments

Comments
 (0)