题目: 有一个源源不断地吐出整数的数据流,假设你有足够的空间来保存吐出的数。请设计一个名叫MedianHolder的结构,MedianHolder可以随时取得之前吐出所有数的中位数。
要求: 如果MedianHolder已经保存了吐出的N个数,那么任意时刻将一个新数加入到MedianHolder的过程,其时间复杂度O(logN)。
取得已经吐出的N个数整体的中位数的过程,时间复杂度O(1)。
基本思路:使用两个堆结构,一个大根堆,一个小根堆。将接收的所有数的较小的一半放入大根堆,将接收的较大的一半放入小根堆。如果接收的个数为奇数,中位数就是小根堆和大根堆中元素数量多的那个堆的堆顶,比如吐出的数是6,1,3,0,9,8,7,小根堆中存放6,7,8,9,大根堆存放0,1,3,小根堆的元素个数多,它的堆顶就是该序列的中位数,即6;如果接收的个数是偶数,中位数就是两个堆顶相加除以2。
每次接收到一个数,都要正确选择放入哪个堆,小于小根堆堆顶的都放入大根堆,否则放入小根堆。如果出现一个堆的元素个数比另一个堆的元素多两个的情况,将前者的堆顶弹出添加到后者中,并重新调整两个堆。总之要始终保持两个堆元素个数的差值不大于1。
这样随时都可以知道已经吐出的所有数处于中间位置的两个数是什么,取得中位数的操作时间复杂度为O(1),同时根据堆的性质,向堆中添加一个新数,并且调整堆的代价为O(logN)。然而题目有一个很重要的限制“任意时刻将一个新数加入到MedianHolder的过程,其时间复杂度O(logN)”
"""
伪代码如下
"""
class MedianHolder:
def __init__(self):
self.maxHeap = maxHeap()
self.minHeap = minHeap()
def addNum(self,num):
if self.maxHeap.isEmpty() or self.maxHeap.peek() > num:
self.maxHeap.add(num)
else:
self.minHeap.add(num)
modifyHeap()
def modifyHeap(self):
if self.maxHeap.size() == self.minHeap.size() + 2:
self.minHeadp.add(self.maxHeap.peek())
if self.maxHeap.size() == self.minHeap.size() - 2:
self.maxHeadp.add(self.minHeap.peek())
def getMedian(self):
if self.maxHeap.isEmpty():
return None
if self.maxHeap.size() == self.minHeap.size():
return (self.maxHeap.peek() + self.minHeap.peek())/2
else:
if self.maxHeap.size() > self.minHeap.size():
return self.maxHeap.peek()
else:
return self.minHeap.peek()