P1168 中位数 vector/priority_queue/权值线段树

代码1注释与解题思路

解题思路

这段代码使用了一个动态维护的有序数组来求解中位数。对于每个新输入的数字,使用二分查找将其插入到正确的位置以保持数组有序。当处理到奇数个元素时,直接输出数组中间位置的元素作为中位数。

代码注释

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
vector<int> a;  // 用于存储有序序列的vector
int n,x;  // n表示数字个数,x用于临时存储输入的数字

int main()
{
    // 读取第一个数字并直接输出(第一个中位数就是它自己)
    cin >> n >> x;
    a.push_back(x);
    cout << x << endl;
    
    // 处理后续数字
    for(int i = 2; i <= n; i++)
    {
        cin >> x;
        // 使用upper_bound找到插入位置,保持数组有序
        a.insert(upper_bound(a.begin(),a.end(),x),x);
        
        // 如果是奇数个数字,输出当前中位数
        if(i % 2 == 1){
            // 输出中间位置的元素
            cout << a[i / 2] << endl;
        }
    }
    return 0;
}

代码2注释与解题思路

解题思路

这段代码使用了两个堆(优先队列)来高效地维护中位数。一个大根堆存储较小的一半数字,一个小根堆存储较大的一半数字。通过平衡两个堆的大小,可以快速获取当前的中位数。这种方法的时间复杂度比第一种更优。

代码注释

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
priority_queue<int,vector<int>,greater<int> > mx;  // 小根堆,存储较大的一半数字
priority_queue<int> mi;  // 大根堆,存储较小的一半数字
int n,x,ans;  // n表示数字个数,x用于临时存储输入的数字

int main()
{
    // 读取第一个数字并直接输出
    cin >> n >> x;
    mx.push(x);  // 初始放入小根堆
    cout << mx.top() << endl;  // 第一个中位数
    
    // 处理后续数字
    for(int i = 2; i <= n; i++)
    {
        cin >> x;
        // 根据数字大小决定放入哪个堆
        if(mx.top() <= x) mx.push(x);
        if(mx.top() > x) mi.push(x);
        
        // 平衡两个堆的大小,保持小根堆大小等于或比大根堆大1
        if(mi.size() + 1 < mx.size()){
            mi.push(mx.top());
            mx.pop();
        }
        else if(mi.size() > mx.size()){
            mx.push(mi.top());
            mi.pop();
        }
        
        // 如果是奇数个数字,输出小根堆的堆顶(当前中位数)
        if(i % 2 == 1)
        {
            cout << mx.top() << endl;
        }
    }
    return 0;
}

代码3注释与解题思路

解题思路

这段代码使用了线段树来统计数字的出现频率,并通过查询线段树来找到中位数。首先对输入数字进行离散化处理,然后使用线段树维护数字的频率。对于奇数个数字,查询线段树找到第k小的数字作为中位数。

代码注释

#include<bits/stdc++.h>
#define lc rt << 1
#define rc rt << 1 | 1
#define lson lc,l,mid
#define rson rc,mid + 1,r
#define ll long long
using namespace std;
const int N = 1e5 + 10;

struct node{
    int sum;  // 线段树节点,记录区间内数字的总数
};

node t[N << 2];  // 线段树数组
int n,a[N],b[N];  // a存储原始数字,b用于离散化

// 线段树更新函数
void pushup(int rt)
{
    t[rt].sum = t[lc].sum + t[rc].sum; 
}

// 线段树单点修改函数
void change(int rt,int l,int r,int x)
{
    if(x < l || r < x) return;  // 超出范围直接返回
    if(l == r){
        t[rt].sum++;  // 找到对应位置,计数+1
        return;
    }
    int mid = (l + r) >> 1;
    change(lson,x);  // 递归修改左子树
    change(rson,x);  // 递归修改右子树
    pushup(rt);  // 更新当前节点
}

// 线段树查询第k小的数
int query(int rt,int l,int r,int k)
{
    if(l == r) return l;  // 找到叶子节点,返回位置
    int mid = (l + r) >> 1;
    if(t[lc].sum >= k) return query(lson,k);  // 左子树数量足够,查询左子树
    else return query(rson,k - t[lc].sum);  // 否则查询右子树
}

int main()
{
    cin >> n;
    // 读取输入并离散化
    for(int i = 1; i <= n; i++)
    {
        cin >> a[i];
        b[i] = a[i];  // 复制到b数组用于离散化
    }
    
    // 排序并去重(离散化)
    sort(b + 1,b + 1 + n);
    unique(b + 1,b + 1 + n);
    
    // 将原始数字映射为离散化后的值
    for(int i = 1; i <= n; i++)
    {
        int x = lower_bound(b + 1,b + 1 + n,a[i]) - b;
        a[i] = x;
    }
    
    // 处理每个数字
    for(int i = 1; i <= n; i++)
    {
        change(1,1,N,a[i]);  // 更新线段树
        if(i % 2 == 1) 
            // 查询中位数并输出原始值
            printf("%d\n",b[query(1,1,N,i / 2 + 1)]);
    }
    return 0;
}

 

posted @ 2025-06-13 15:06  CRt0729  阅读(35)  评论(0)    收藏  举报