【每日一题】12.Running Median (对顶堆)

补题链接:Here

题意:动态的维护中位数的问题,依次读入一个整数,每当总个数为奇数时输出此时序列的中位数

使用对顶堆的在线做法。

为了实时找到中位数,我们可以建议两个二叉堆:一个小根堆、一个大根堆。在依次读入数字的过程设当前序列长度为 \(M\),我们始终保持:

  1. 序列中从小到大排名为 \(1\) ~ $ M/2 $ 的整数存储在大根堆中;
  2. 序列中从小到大排名为 \(M/2 + 1\) ~ \(M\) 的整数存储在小根堆中。

任何时候如果某一个堆中元素过多则是打破了平衡需要取出该堆的堆顶插入到另一个堆。这样一来序列的中位数就是小根堆的堆顶

关于新数 \(X\) 插入:如果 \(X\) 比中位数小,则插入大根堆,否则就插入小根堆。之后再维护平衡即可

上述算法就是 “对顶堆” 算法

这个代码会爆空间,但很好的实现的上述过程

priority_queue<int> q1, q2;
void solve() {
    while (q1.size()) q1.pop();
    while (q2.size()) q2.pop();

    int num, n;
    cin >> num >> n;
    cout << num << " " << (n + 1) / 2 << "\n";
    int a;
    cin >> a;
    cout << a << " ";
    q2.push(-a);
    int cnt = 1;
    for (int i = 2; i <= n; ++i) {
        cin >> a;
        if (a < -q2.top()) q1.push(a);
        else
            q2.push(-a);
        int s = q1.size();
        if (s > i / 2) {
            q2.push(-q1.top());
            q1.pop();
        }
        if (s < i / 2) {
            q1.push(-q2.top());
            q2.pop();
        }
        if (i & 1) {
            cout << -q2.top() << " ";
            if (++cnt % 10 == 0) cout << endl;
        }
    }
    cout << "\n";
}

AC 代码

vector<short> a;
void solve() {
    a.clear();
    int k, n;
    cin >> k >> n;
    cout << k << " " << (n + 1) / 2 << "\n";
    for (int i = 1; i <= n; ++i) {
        short x;
        cin >> x;
        a.insert(upper_bound(a.begin(), a.end(), x), x);
        if (i & 1) cout << a[(i - 1) / 2] << " ";
        if (i % 20 == 0) cout << "\n";
    }
    cout << "\n";
}
posted @ 2021-04-20 21:51  RioTian  阅读(8)  评论(0编辑  收藏