从零开始实现堆,获取数据流中的中位数。中位数可能由两位数决定,所以需要两个堆。
中位数特点:对于排好序的序列,它是左半部分的最大值,是右半部分的最小值。当元素个数是偶数时,左右半部分元素个数相等,中位数是左边最大和右边最小的平均,当元素个数是奇数时,让左半部分多一个元素,那么中位数就是左半部分的最大值。
那么左边维护一个大根堆,右边维护一个小根堆,并且维护大根堆的堆顶元素不大于小根堆的堆顶元素。
接收到第奇数个数据,插入大根堆,接收到第偶数个数,插入小根堆。
# 中位数特点,左边的最大值,右边的最小值 # 维护两个堆,一个大根堆,一个小根堆,要求大根堆堆顶元素小于等于小根堆堆顶元素 class Solution: def __init__(self): self.left = [] # 数据流排序后左半部分 self.right = [] # 数据流排序后右半部分 # 大根堆调整函数 @staticmethod def heapAdjust(lis, i, end): j = 2*i + 1 while j < end: if j+1 < end and lis[j+1] > lis[j]: j += 1 if lis[j] > lis[i]: lis[i], lis[j] = lis[j], lis[i] i = j j = 2*i + 1 else: break # 大根堆插入函数 @staticmethod def heappush(lis, num): # 尾插 lis.append(num) j = len(lis) - 1 # 插入节点的下标 # 调整堆 while j > 0: i = (j-1)//2 # 定位父节点下标 if lis[i] < lis[j]: # 父小于子,交换 lis[i], lis[j] = lis[j], lis[i] j = i else: break def heappop(self, lis): lis[0], lis[-1] = lis[-1], lis[0] pop = lis.pop() self.heapAdjust(lis, 0, len(lis)-1) return pop def Insert(self, num): # 保证右边的值比左边大,奇数个数填左,保证奇数时左边多一个 # 当前接收到数据个数总数为奇数,左边加值,但是加的值必须不大于右边最小值(堆顶)。 # 如果接收到的数据不满足条件,则与右边最小值交换,再各自插入。 if len(self.left) == len(self.right): # 原先左右相等,当前是奇数个 if not self.left: self.heappush(self.left, num) elif num > -self.right[0]: self.heappush(self.left, -self.heappop(self.right)) self.heappush(self.right, -num) else: self.heappush(self.left, num) # 当前接收到数据个数总数为偶数,右边加值,但是加的值必须不小于左边的最大值(堆顶)。 # 如果接收到的数据不满足条件,则与左边最大值交换,再各自插入。 else: if num < self.left[0]: self.heappush(self.right, -self.heappop(self.left)) self.heappush(self.left, num) else: self.heappush(self.right, -num) def GetMedian(self): # 插入完成后self.odd被改变,所以这里not self.odd 表示接收到数据个数为奇数 return self.left[0] if len(self.left)!=len(self.right) else (self.left[0] -self.right[0])/2