剑指Offer41-数据流中的中位数

剑指Offer41-数据流中的中位数

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

例如,

[2,3,4] 的中位数是 3

[2,3] 的中位数是 (2 + 3) / 2 = 2.5

设计一个支持以下两种操作的数据结构:

void addNum(int num) - 从数据流中添加一个整数到数据结构中。

double findMedian() - 返回目前所有元素的中位数。

示例 1:


输入:

["MedianFinder","addNum","addNum","findMedian","addNum","findMedian"]

[[],[1],[2],[],[3],[]]

输出:[null,null,null,1.50000,null,2.00000]

示例 2:


输入:

["MedianFinder","addNum","findMedian","addNum","findMedian"]

[[],[2],[],[3],[]]

输出:[null,null,2.00000,null,2.50000]

限制:

  • 最多会对 addNum、findMedian 进行 50000 次调用。

来源:力扣(LeetCode)

链接:力扣

一解

使用两个堆,大顶堆和小顶堆求中位数:

图片来自 jyd

Python 的 heapq 模块是小顶堆。实现大顶堆需要“小顶堆的插入和弹出操作”均将“元素取反”,即heappushpop(self.B, -num)

不清楚大小顶堆的,注意以下代码的输出结果:


from heapq import *

a = []

b = []

heappush(a, 10)

heappush(a, 60)

heappush(a, 70)

print(a)

print("push 30 to a")

heappush(b,  -heappushpop(a, 30))

print(a)

print(b)

heappush(a,  -heappushpop(a, -40))

print(a)

print(b)


[10, 60, 70]

push 30 to a

[30, 60, 70]

[-10]

[30, 40, 70, 60]

[-10]


from heapq import *

class MedianFinder:

    def __init__(self):

        self.A = [] # 小顶堆,保存较大的一半

        self.B = [] # 大顶堆,保存较小的一半

    def addNum(self, num: int) -> None:

        if len(self.A) != len(self.B):

            # 将 A 的最小值放入 B 中

            heappush(self.B, -heappushpop(self.A, num))

        else:

            # 将 B 的最大值放入 A 中

            heappush(self.A, -heappushpop(self.B, -num))

    def findMedian(self) -> float:

        return self.A[0] if len(self.A) != len(self.B) else (self.A[0] - self.B[0]) / 2.0