返回

数据流的中位数快速实现

闲谈

概述
在数据流中计算中位数是一个常见问题。中位数是指将所有数据点从小到大排序后,位于中间位置的点。当数据点个数为奇数时,中位数就是位于中间位置的点;当数据点个数为偶数时,中位数就是位于中间两个点之间的平均值。

数据流是一种随着时间不断更新的实时数据,因此需要一种快速高效的算法来计算中位数。本文将介绍几种常用的算法,并对它们在不同情况下的性能进行比较。最后,将给出一种基于堆的数据结构实现的快速中位数算法。

算法

1. 暴力算法

最简单的中位数算法是暴力算法。该算法首先将所有数据点从小到大排序,然后根据数据点个数的奇偶性计算中位数。当数据点个数为奇数时,中位数就是位于中间位置的点;当数据点个数为偶数时,中位数就是位于中间两个点之间的平均值。

暴力算法的时间复杂度为O(nlogn),其中n是数据流中的数据点个数。这是因为排序操作的时间复杂度为O(nlogn)。

2. 基于插入排序的中位数算法

基于插入排序的中位数算法是一种改进的暴力算法。该算法在将数据点插入到排序数组中的同时计算中位数。当数据点个数为奇数时,中位数就是位于中间位置的点;当数据点个数为偶数时,中位数就是位于中间两个点之间的平均值。

基于插入排序的中位数算法的时间复杂度为O(n^2),其中n是数据流中的数据点个数。这是因为插入操作的时间复杂度为O(n)。

3. 基于堆的中位数算法

基于堆的中位数算法是一种快速高效的中位数算法。该算法使用一个堆来存储数据点。堆是一种二叉树,其中每个节点的值都大于或等于其子节点的值。当数据点插入到堆中时,堆会自动将数据点调整到正确的位置。

基于堆的中位数算法的时间复杂度为O(logn),其中n是数据流中的数据点个数。这是因为堆的插入操作和删除操作的时间复杂度都为O(logn)。

比较

下表比较了三种中位数算法的时间复杂度和空间复杂度。

算法 时间复杂度 空间复杂度
暴力算法 O(nlogn) O(n)
基于插入排序的中位数算法 O(n^2) O(n)
基于堆的中位数算法 O(logn) O(n)

从表中可以看出,基于堆的中位数算法是最快的,时间复杂度为O(logn)。而基于插入排序的中位数算法是最慢的,时间复杂度为O(n^2)。

实现

下面给出了基于堆的中位数算法的Python实现:

class MedianFinder:

    def __init__(self):
        self.min_heap = []  # 小顶堆,存储较大的元素
        self.max_heap = []  # 大顶堆,存储较小的元素

    def addNum(self, num: int) -> None:
        if len(self.min_heap) == len(self.max_heap):
            heappush(self.max_heap, -heappushpop(self.min_heap, num))
        else:
            heappush(self.min_heap, -heappushpop(self.max_heap, -num))

    def findMedian(self) -> float:
        if len(self.min_heap) == len(self.max_heap):
            return (self.min_heap[0] - self.max_heap[0]) / 2
        else:
            return self.min_heap[0]


# 测试
median_finder = MedianFinder()
median_finder.addNum(1)
median_finder.addNum(2)
print(median_finder.findMedian())  # 输出:1.5
median_finder.addNum(3)
print(median_finder.findMedian())  # 输出:2.0
median_finder.addNum(4)
print(median_finder.findMedian())  # 输出:2.5

结语

本文介绍了三种中位数算法:暴力算法、基于插入排序的中位数算法和基于堆的中位数算法。其中,基于堆的中位数算法是最快的,时间复杂度为O(logn)。