目录

0295:数据流的中位数(★★)

力扣第 295 题

题目

中位数是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。

  • 例如 arr = [2,3,4] 的中位数是 3
  • 例如 arr = [2,3] 的中位数是 (2 + 3) / 2 = 2.5

实现 MedianFinder 类:

  • MedianFinder() 初始化 MedianFinder 对象。

  • void addNum(int num) 将数据流中的整数 num 添加到数据结构中。

  • double findMedian() 返回到目前为止所有元素的中位数。与实际答案相差 10-5 以内的答案将被接受。

示例 1:

输入
["MedianFinder", "addNum", "addNum", "findMedian", "addNum", "findMedian"]
[[], [1], [2], [], [3], []]
输出
[null, null, null, 1.5, null, 2.0]

解释
MedianFinder medianFinder = new MedianFinder();
medianFinder.addNum(1);    // arr = [1]
medianFinder.addNum(2);    // arr = [1, 2]
medianFinder.findMedian(); // 返回 1.5 ((1 + 2) / 2)
medianFinder.addNum(3);    // arr[1, 2, 3]
medianFinder.findMedian(); // return 2.0

提示:

  • -105 <= num <= 105
  • 在调用 findMedian 之前,数据结构中至少有一个元素
  • 最多 5 * 104 次调用 addNumfindMedian

分析

#1

最简单的是用 SortedList 维护一个有序集合即可。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class MedianFinder:

    def __init__(self):
        from sortedcontainers import SortedList
        self.sl = SortedList()


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


    def findMedian(self) -> float:
        n = len(self.sl)
        return (self.sl[(n-1)//2]+self.sl[n//2])/2

439 ms

#2

也可以用双堆维护:

  • 用两个堆 A、B 分别维护较小的一半和较大的一半
  • A 要弹出最大数,B 要弹出最小数,因此 A 为大顶堆,注意入堆时元素取负
  • 为了方便,总个数为奇数时,多余的一个放在 A
  • addNum 时
    • 先将元素添加到 A 中,弹出堆顶并加到 B 中
    • 如果 len(A)<len(B),就再弹出 B 堆顶加到 A 中
  • findMedian 时,根据奇偶情况返回即可

解答

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class MedianFinder:

    def __init__(self):
        self.A = []
        self.B = []

    def addNum(self, num: int) -> None:
        heappush(self.A,-num)
        heappush(self.B,-heappop(self.A))
        if len(self.B)>len(self.A):
            heappush(self.A,-heappop(self.B))

    def findMedian(self) -> float:
        if len(self.A)>len(self.B):
            return -self.A[0]
        return (self.B[0]-self.A[0])/2

330 ms