LeetCode 295

https://leetcode.cn/problems/find-median-from-data-stream/description/

难度:困难

高频面试题汇总:https://www.yuweihung.com/posts/2025/lc-hot/

需要两个堆。left 是最大堆,right 是最小堆。

保证 left 的大小和 right 的大小尽量相等。规定:在有奇数个数时,left 比 right 多 1 个数。

保证 left 的所有元素都小于等于 right 的所有元素。

如果当前有奇数个元素,中位数是 left 的堆顶。

如果当前有偶数个元素,中位数是 left 的堆顶和 right 的堆顶的平均值。

如果当前 left 的大小和 right 的大小相等:

  • 如果添加的数字 num 比较大,比如添加 7,那么把 7 加到 right 中。现在 left 比 right 少 1 个数,不符合前文的规定,所以必须把 right 的最小值从 right 中去掉,添加到 left 中。如此操作后,可以保证 left 的所有元素都小于等于 right 的所有元素。
  • 如果添加的数字 num 比较小,比如添加 0,那么把 0 加到 left 中。
  • 这两种情况可以合并:无论 num 是大是小,都可以先把 num 加到 right 中,然后把 right 的最小值从 right 中去掉,并添加到 left 中。

如果当前 left 比 right 多 1 个数:

  • 如果添加的数字 num 比较大,比如添加 7,那么把 7 加到 right 中。
  • 如果添加的数字 num 比较小,比如添加 0,那么把 0 加到 left 中。现在 left 比 right 多 2 个数,不符合前文的规定,所以必须把 left 的最大值从 left 中去掉,添加到 right 中。如此操作后,可以保证 left 的所有元素都小于等于 right 的所有元素。
  • 这两种情况可以合并:无论 num 是大是小,都可以先把 num 加到 left 中,然后把 left 的最大值从 left 中去掉,并添加到 right 中。

时间复杂度:初始化和 findMedian 都是 O(1),addNum 是 O(logq),其中 q 是 addNum 的调用次数。每次操作堆需要 O(logq) 的时间。

空间复杂度:O(q)。

class MedianFinder {
    priority_queue<int> left; // 最大堆
    priority_queue<int, vector<int>, greater<>> right; // 最小堆

public:
    void addNum(int num) {
        if (left.size() == right.size()) {
            right.push(num);
            left.push(right.top());
            right.pop();
        } else {
            left.push(num);
            right.push(left.top());
            left.pop();
        }
    }

    double findMedian() {
        if (left.size() > right.size()) {
            return left.top();
        }
        return (left.top() + right.top()) / 2.0;
    }
};