返回

如何使用大小堆解决数据流中位数问题

前端

算法流程:

  1. 初始化两个堆:大顶堆和小顶堆。大顶堆保存的是数据流中较大的部分,小顶堆保存的是数据流中较小的部分。
  2. 将数据流中的第一个数添加到大顶堆中。
  3. 将数据流中的第二个数添加到小顶堆中。
  4. 判断大顶堆和小顶堆的元素个数是否相等。
  5. 如果大顶堆和小顶堆的元素个数相等,则数据流中的中位数是这两个堆的堆顶元素的平均值。
  6. 如果大顶堆的元素个数多于小顶堆的元素个数,则数据流中的中位数是大顶堆的堆顶元素。
  7. 如果小顶堆的元素个数多于大顶堆的元素个数,则数据流中的中位数是小顶堆的堆顶元素。
  8. 将数据流中的下一个数添加到对应堆中,并重复步骤4-7。

时间复杂度:
该算法的时间复杂度是 O(log N),因为在每次添加一个数到堆中时,只需要对堆进行一次调整,而调整堆的时间复杂度是 O(log N)。

空间复杂度:
该算法的空间复杂度是 O(N),因为需要存储数据流中的所有数。

代码实现:

import heapq

class MedianFinder:
    def __init__(self):
        self.max_heap = []  # 大顶堆
        self.min_heap = []  # 小顶堆

    def add_num(self, num):
        if len(self.max_heap) == len(self.min_heap):
            heapq.heappush(self.max_heap, -heapq.heappushpop(self.min_heap, num))
        else:
            heapq.heappush(self.min_heap, heapq.heappushpop(self.max_heap, -num))

    def find_median(self):
        if len(self.max_heap) == len(self.min_heap):
            return (self.min_heap[0] - self.max_heap[0]) / 2
        else:
            return self.max_heap[0] * -1


if __name__ == '__main__':
    mf = MedianFinder()
    mf.add_num(1)
    mf.add_num(2)
    mf.add_num(3)
    print(mf.find_median())  # 2.0
    mf.add_num(4)
    print(mf.find_median())  # 2.5

使用技巧:

  • 可以使用大小堆来解决其他数据流问题,例如数据流最大值、数据流最小值、数据流中位数的滑动窗口等。
  • 可以使用大小堆来实现优先级队列,优先级队列是一种按照元素的优先级来存储元素的数据结构。