题解:学而思编程 动态中位数

【题目来源】

学而思编程:动态中位数

【题目描述】

依次读入一个整数序列,每当已经读入的整数个数为奇数时,输出已读入的整数构成的序列的中位数。

【输入】

第一行一个整数 \(N(1≤N≤105)\),表示序列长度。

第二行 \(N\) 个整数 \(A_i(1≤A_i≤10^9)\)

【输出】

输出 \(⌊\displaystyle\frac{N+1}{2}⌋\) 行(向下取整),第 \(i\) 行为 \(A_1\)\(A_{2i−1}\) 的中位数。

【输入样例】

7
1 3 5 7 9 11 6

【输出样例】

1
3
5
6

【核心思想】

  1. 问题分析:依次读入 \(N\) 个整数,每当已读入的整数个数为奇数时,输出当前序列的中位数。这是一个动态中位数问题,需要在数据流中实时维护中位数。

  2. 算法选择

    • 双堆策略(大根堆 + 小根堆):使用两个优先队列(堆)来维护数据流
    • 大根堆(small):存储较小的一半数字,堆顶为较小部分的最大值
    • 小根堆(big):存储较大的一半数字,堆顶为较大部分的最小值
    • 平衡维护:确保两个堆的大小差不超过 \(1\),且大根堆的大小始终大于等于小根堆
  3. 关键步骤

    • 初始化:创建大根堆 small 和小根堆 big
    • 插入元素(处理第 \(i\) 个元素 \(A_i\)):
      • 如果 small 非空且 $A_i > $ small.top(),则将 \(A_i\) 放入 big
      • 否则将 \(A_i\) 放入 small
    • 奇数时输出中位数(当 \(i\) 为奇数时):
      • 调整堆大小:确保 small.size() = big.size() + 1
      • 如果 small.size() < big.size(),将 big.top() 移到 small
      • 中位数为 small.top(),输出
    • 偶数时平衡堆(当 \(i\) 为偶数时):
      • 调整堆大小:确保 small.size() = big.size()
      • 如果 small.size() < big.size(),将 big.top() 移到 small
      • 如果 small.size() > big.size(),将 small.top() 移到 big
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N \log N)\),每次插入和堆调整操作均为 \(O(\log N)\),共 \(N\) 次操作
    • 空间复杂度:\(O(N)\),需要存储所有 \(N\) 个元素在两个堆中
  5. 双堆维护中位数的核心思想

    • 数据分流:根据当前元素与堆顶的比较,决定放入哪个堆,保持有序性
    • 大小平衡:通过控制两个堆的大小关系,确保中位数始终在大根堆的堆顶
    • 动态调整:每次插入后根据奇偶性调整堆的大小,保证中位数的正确性
    • 中位数定位:当元素总数为奇数时,中位数是较大一半的最小值(大根堆顶)
    • 适用于数据流中位数查询、动态序列统计类问题

【算法标签】

堆排序

【代码详解】

#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;

// 小根堆存储较大的一半数字
priority_queue<int> small;
// 大根堆存储较小的一半数字
priority_queue<int, vector<int>, greater<int>> big;
int n, a;

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> a;
        // 根据当前数字与small堆顶比较,决定放入哪个堆
        if (!small.empty() && a > small.top())
        {
            big.push(a);
        }
        else
        {
            small.push(a);
        }

        // 当i为奇数时,输出中位数
        if (i % 2)
        {
            // 调整两个堆的大小,确保small堆的大小比big堆大1
            if (small.size() < big.size())
            {
                small.push(big.top());
                big.pop();
            }
            cout << small.top() << endl;  // 中位数是small堆的堆顶
        }
        else
        {
            // 当i为偶数时,平衡两个堆的大小
            if (small.size() < big.size())
            {
                small.push(big.top());
                big.pop();
            }
            else if (small.size() > big.size())
            {
                big.push(small.top());
                small.pop();
            }
        }
    }
    return 0;
}

【运行结果】

7
1 3 5 7 9 11 6
1
3
5
6
posted @ 2026-06-14 10:46  团爸讲算法  阅读(11)  评论(0)    收藏  举报