refer to: https://www.algoexpert.io/questions/Continuous%20Median
Problem statement
Analysis
O(N) for insert one number into a sorted array
Code
def MAX_HEAP_FUNC(a, b): return a > b def MIN_HEAP_FUNC(a, b): return a < b class ContinuousMedianHandler: def __init__(self): self.lowers = Heap(MAX_HEAP_FUNC, []) #create a maxheap for lower half of the numbers self.greaters = Heap(MIN_HEAP_FUNC, []) #create a minheap for greater half of the numbers self.median = None def insert(self, number): if not self.lowers.length or number < self.lowers.peek():#empty lower heap or the new number < the peek of the lower maxheap self.lowers.insert(number) else: self.greaters.insert(number) self.rebalanceHeaps() # balance the lower and greater heaps, make sure the different between their lengths <= 1 self.updateMedian() #update new median value def rebalanceHeaps(self): if self.lowers.length - self.greaters.length == 2: # lower heap has more values self.greater.insert(self.lowers.remove())# remove the peek(max value) of the lower heap into the greater heap elif self.greaters.length - self.lowers.length == 2: self.lowers.insert(self.greaters.remove()) def updateMedian(self): if self.lowers.length == self.greaters.length: # same length, the two mid values are peeks of low and great heaps self.median = (self.lowers.peek() + self.greaters.peek())/2 elif self.lowers.length > self.greaters.length: # lower heap has more one valye, the peek one of lower heap self.median = self.lowers.peek() else: # higher heap has more one value self.median = self.greaters.peek() def getMedian(self): return self.median
Time and space complexity
time: log(n) time for insert, remove in heaps.
space: store all the numbers in heap->O(N)
The Heap class
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]