数据流的中位数快速实现
2023-11-11 07:16:35
概述
在数据流中计算中位数是一个常见问题。中位数是指将所有数据点从小到大排序后,位于中间位置的点。当数据点个数为奇数时,中位数就是位于中间位置的点;当数据点个数为偶数时,中位数就是位于中间两个点之间的平均值。
数据流是一种随着时间不断更新的实时数据,因此需要一种快速高效的算法来计算中位数。本文将介绍几种常用的算法,并对它们在不同情况下的性能进行比较。最后,将给出一种基于堆的数据结构实现的快速中位数算法。
算法
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)。