1+ """
2+ Median in a stream using a heap-based approach.
3+
4+ Reference:
5+ https://en.wikipedia.org/wiki/Median#Running_median
6+ """
7+
18import heapq
29
310
411def signum (a : int , b : int ) -> int :
512 """
6- Compare two integers.
7-
8- Returns:
9- 1 if a > b
10- -1 if a < b
11- 0 if a == b
13+ Return 1 if a > b, -1 if a < b, 0 if equal.
1214 """
1315 if a > b :
1416 return 1
@@ -24,54 +26,51 @@ def call_median(
2426 median : int ,
2527) -> int :
2628 """
27- Insert an element into heaps and update the median.
29+ Update heaps and median based on the new element.
30+
31+ Args:
32+ element (int): new element in stream
33+ max_heap (list[int]): max heap (as negative numbers)
34+ min_heap (list[int]): min heap
35+ median (int): current median
36+
37+ Returns:
38+ int: updated median
2839 """
29- case = signum (len (max_heap ), len (min_heap ))
40+ size_diff = signum (len (max_heap ), len (min_heap ))
3041
31- if case == 0 :
42+ if size_diff == 0 :
3243 if element > median :
3344 heapq .heappush (min_heap , element )
3445 median = min_heap [0 ]
3546 else :
3647 heapq .heappush (max_heap , - element )
3748 median = - max_heap [0 ]
38-
39- elif case == 1 :
49+ elif size_diff == 1 :
4050 if element > median :
4151 heapq .heappush (min_heap , element )
4252 else :
43- heapq .heappush (min_heap , - heapq .heappop (max_heap ))
44- heapq .heappush (max_heap , - element )
53+ heapq .heappush (min_heap , - heapq .heappushpop (max_heap , - element ))
4554 median = (- max_heap [0 ] + min_heap [0 ]) // 2
46-
47- else :
55+ else : # size_diff == -1
4856 if element > median :
49- heapq .heappush (max_heap , - heapq .heappop (min_heap ))
50- heapq .heappush (min_heap , element )
57+ heapq .heappush (max_heap , - heapq .heappushpop (min_heap , element ))
5158 else :
5259 heapq .heappush (max_heap , - element )
5360 median = (- max_heap [0 ] + min_heap [0 ]) // 2
5461
5562 return median
5663
5764
58- def median_in_a_stream (numbers : list [int ]) -> list [int ]:
65+ def median_in_a_stream (arr : list [int ]) -> list [int ]:
5966 """
60- Find the median after each insertion in a stream of integers.
61-
62- Uses two heaps and follows the classic running median logic.
63-
64- Reference:
65- https://en.wikipedia.org/wiki/Median#Running_median
67+ Return the median after each new element in the stream.
6668
6769 Args:
68- numbers: List of integers
70+ arr (list[int]): list of integers
6971
7072 Returns:
71- List of medians after each insertion
72-
73- Raises:
74- ValueError: If the input list is empty
73+ list[int]: running medians
7574
7675 >>> median_in_a_stream([20, 14, 13, 16, 17])
7776 [20, 17, 14, 15, 16]
@@ -82,16 +81,24 @@ def median_in_a_stream(numbers: list[int]) -> list[int]:
8281 ...
8382 ValueError: Input list must not be empty
8483 """
85- if not numbers :
84+ if not arr :
8685 raise ValueError ("Input list must not be empty" )
8786
88- max_heap : list [int ] = []
89- min_heap : list [int ] = []
90- median = 0
91- result : list [int ] = []
87+ max_heap : list [int ] = [] # left side (as negative numbers)
88+ min_heap : list [int ] = [] # right side
89+ median = arr [0 ]
90+ max_heap .append (- arr [0 ])
91+ medians : list [int ] = [median ]
9292
93- for element in numbers :
93+ for element in arr [ 1 :] :
9494 median = call_median (element , max_heap , min_heap , median )
95- result .append (median )
95+ medians .append (median )
96+
97+ return medians
98+
9699
97- return result
100+ if __name__ == "__main__" :
101+ n = int (input ("Enter number of elements: " ).strip ())
102+ arr = [int (input ().strip ()) for _ in range (n )]
103+ result = median_in_a_stream (arr )
104+ print ("Running medians:" , result )
0 commit comments