-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathContinous Median.py
120 lines (100 loc) · 4.28 KB
/
Continous Median.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
"""
Continuous Median
Write a ContinuousMedianHandler class that supports:
• The continuous insertion of numbers with the insert method.
• The instant (O(1) time) retrieval of the median of the numbers that have been inserted thus far with the getMedian method.
The getMedian method has already been written for you. You simply have to write the insert method.
The median of a set of numbers is the "middle" number when the numbers are ordered from smallest to greatest. If there's an odd
number of numbers in the set, as in {1, 3, 7} , the median is the number in the middle ( 3 in this case); if there's an even number of
numbers in the set, as in {1, 3, 7, 8} , the median is the average of the two middle numbers ( (3 + 7) / 2 == 5 in this case).
Sample Usage
// All operations below are performed sequentially.
ContinuousMedianHandler(): - // instantiate a continuousMedianHandler
insert(5):
insert(10):
getMedian(): 7.5
insert(100):
getMedian(): 10
"""
# SOLUTION 1
class ContinuousMedianHandler:
def __init__(self):
self.lowers = Heap(MAX_HEAP_FUNC, [])
self.greaters = Heap(MIN_HEAP_FUNC, [])
self.median = None
# O(log(n)) time | O(n) space
def insert(self, number):
if not self.lowers.length or number < self.lowers.peek():
self.lowers.insert(number)
else:
self.greaters.insert(number)
self.rebalanceHeaps()
self.updateMedian()
def rebalanceHeaps(self):
if self.lowers.length - self.greaters.length == 2:
self.greaters.insert(self.lowers.remove())
elif self.greaters.length - self.lowers.length == 2:
self.lowers.insert(self.greaters.remove())
def updateMedian(self):
if self.lowers.length == self.greaters.length:
self.median = (self.lowers.peek() + self.greaters.peek()) / 2
elif self.lowers.length > self.greaters.length:
self.median = self.lowers.peek()
else:
self.median = self.greaters.peek()
def getMedian(self):
return self.median
class Heap:
def __init__(self, comparisonFunc, array):
self.comparisonFunc = comparisonFunc
self.heap = self.buildHeap(array)
self.length = len(self.heap)
def buildHeap(self, array):
firstParentIdx = (len(array) - 2) // 2
for currentIdx in reversed(range(firstParentIdx + 1)):
self.siftDown(currentIdx, len(array) - 1, array)
return array
def siftDown(self, currentIdx, endIdx, heap):
childOneIdx = currentIdx * 2 + 1
while childOneIdx <= endIdx:
childTwoIdx = currentIdx * 2 + 2 if currentIdx * 2 + 2 <= endIdx else -1
if childTwoIdx != -1:
if self.comparisonFunc(heap[childTwoIdx], heap[childOneIdx]):
idxToSwap = childTwoIdx
else:
idxToSwap = childOneIdx
else:
idxToSwap = childOneIdx
if self.comparisonFunc(heap[idxToSwap], heap[currentIdx]):
self.swap(currentIdx, idxToSwap, heap)
currentIdx = idxToSwap
childOneIdx = currentIdx * 2 + 1
else:
return
def siftUp(self, currentIdx, heap):
parentIdx = (currentIdx - 1) // 2
while currentIdx > 0:
if self.comparisonFunc(heap[currentIdx], heap[parentIdx]):
self.swap(currentIdx, parentIdx, heap)
currentIdx = parentIdx
parentIdx = (currentIdx - 1) // 2
else:
return
def peek(self):
return self.heap[0]
def remove(self):
self.swap(0, self.length - 1, self.heap)
valueToRemove = self.heap.pop()
self.length -= 1
self.siftDown(0, self.length - 1, self.heap)
return valueToRemove
def insert(self, value):
self.heap.append(value)
self.length += 1
self.siftUp(self.length - 1, self.heap)
def swap(self, i, j, array):
array[i], array[j] = array[j], array[i]
def MAX_HEAP_FUNC(a, b):
return a > b
def MIN_HEAP_FUNC(a, b):
return a < b