Featured image of post Find Median From Data Stream

Find Median From Data Stream

295. 数据流的中位数

分析

为了高效地获取中位数,使用 两个优先队列(堆) 来维护数据流的有序性:

  1. down 堆(大顶堆):
    • 存储较小的一半元素,堆顶是最大值
  2. up 堆(小顶堆):
    • 存储较大的一半元素,堆顶是最小值

通过以下规则维持堆的平衡和正确性:

  1. 插入元素:
    • 如果当前元素小于或等于 down 堆的堆顶,将其插入 down
    • 否则,将其插入 up
  2. 调整堆的平衡:
    • 如果 down 的元素个数比 up2,将 down 堆顶移到 up
    • 如果 up 的元素个数比 down1,将 up 堆顶移到 down
  3. 计算中位数:
    • 如果两个堆的元素个数相等,中位数为两个堆顶的平均值;
    • 如果 down 堆的元素个数比 up1,中位数为 down 堆顶的值

时间复杂度

  • 插入操作:O(logn) (堆的插入与删除)
  • 获取中位数:O(1)

总体复杂度:O(logn) (单次操作)

空间复杂度

使用两个堆存储所有元素,空间复杂度为 O(n)

C++代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class MedianFinder {
public:
    // 大顶堆,存储较小的一半元素
    std::priority_queue<int> down;
    // 小顶堆,存储较大的一半元素
    std::priority_queue<int, std::vector<int>, std::greater<int>> up;

    MedianFinder() {}

    void addNum(int num)
    {
        // 插入到适当的堆
        if (down.empty() || num <= down.top())
            down.push(num);
        else
            up.push(num);

        // 调整堆的平衡
        if (down.size() == up.size() + 2)
        {
            int x = down.top();
            down.pop();
            up.push(x);
        }
        else if (down.size() + 1 == up.size())
        {
            int x = up.top();
            up.pop();
            down.push(x);
        }
    }

    double findMedian()
    {
        if (down.size() == up.size())
            return (down.top() + up.top()) / 2.0;
        return down.top();
    }
};
Built with Hugo
Theme Stack designed by Jimmy