动态中位数

动态中位数

题目描述

在这里插入图片描述


样例解释

比如序列1 2 3 4 5 6 7 8 9。当读入的整数个数为奇数时,输出已输入序列的中位数,为偶数时,不用输出。因为是动态输入,当输入1时,数据个数为奇数,此时中位数为1,所以输出1;当输入2时,数据个数为偶数,不需要输出;当输入3时,数据个数为奇数,此时中位数为2,所以输出2;当输入4时,数据个数为偶数,不需要输出;当输入5时,数据个数为奇数,此时中位数为3,所以输出3;当输入6时,数据个数为偶数,不需要输出;当输入7时,数据个数为奇数,此时中位数为4,所以输出4;当输入8时,数据个数为偶数,不需要输出;当输入9时,数据个数为奇数,此时中位数为5,所以输出5


核心思路

将序列的前一半和后一半分开来存放,比如1~n这n个数(假设n为奇数),将1到 n / 2 + 1 n/2+1 n/2+1放在一起,将 n / 2 + 2 n/2+2 n/2+2 n n n放在一起,这样中位数就是 1 1 1 n / 2 + 1 n/2+1 n/2+1中最大的那个。比如当n=5,那么将1,2,3放到一起,将4,5放在一起,那么此时中位数就是1,2,3这一段中最大的那个,即中位数是3。因此,我们的做法是使用对顶堆,使用大根堆来存放1到 n / 2 + 1 n/2+1 n/2+1的这些数,使用小根堆来存放 n / 2 + 2 n/2+2 n/2+2 n n n的这些数。此时大根堆的堆顶元素就是中位数。如果我们是把1到 n / 2 n/2 n/2这些数放到大根堆,把 n / 2 + 1 n/2+1 n/2+1 n n n这些数放到小根堆,那么此时小根堆的堆顶元素就是中位数。

在这里,我们采用第一种,即大根堆的堆顶元素是中位数。设up为小根堆,down为大根堆。

  • 序列中从小到大排名为1到 n / 2 + 1 n/2+1 n/2+1的整数存放到大根堆中
  • 序列中从小到大排名为 n / 2 + 2 n/2+2 n/2+2 n n n的整数存放到小根堆中。
  • 也就是说小根堆中的堆顶元素 ≥ \geq 大根堆的堆顶元素。小根堆中的元素总体上来说是 ≥ \geq 大根堆中的元素。
  • 大根堆的元素个数最多比小根堆的元素个数多1。即down.size() ≤ \leq up.size()+1。

当新来了一个元素x,如果 d o w n . t o p ( ) ≥ x down.top()\geq x down.top()x,则说明 x x x应该放在大根堆 d o w n down down中;如果 d o w n . t o p ( ) < x down.top()<x down.top()<x,则说明 x x x应该放在小根堆 u p up up中。

当大根堆中的元素个数与小根堆中的元素个数相差大于1时,说明大根堆中的元素太多了,此时规定把大根堆的堆顶元素放入小根堆中。

当小根堆的元素个数比大根堆的元素个数多时(按规定应该是大根堆比小根堆多),那么此时规定把小根堆的堆顶元素放入大根堆中。


#include<iostream>
#include<queue>
using namespace std;
int main()
{
    int T;
    scanf("%d",&T);		//输入T组测试数据
    while(T--)
    {
        int n,m;		//m代表数据集的编号,n表示数据集的个数
        scanf("%d%d",&m,&n);
        printf("%d %d\n",m,(n+1)/2);	//输出编号和中位数的个数
        priority_queue<int>down;//大根堆
        priority_queue<int,vector<int>,greater<int>>up; //小根堆
        int cnt=0;		//记录一行中已经输出的个数
        //处理这n个数据
        for(int i=1;i<=n;i++)
        {
            int x;
            scanf("%d",&x);//输入一个数据x
            //如果大根堆为空或者是新来的数据x小于等于此时大根堆的堆顶元素,那么就放到大根堆down中
            if(down.empty()||down.top()>=x)
            down.push(x);
            //如果小根堆为空或者是新来的数据x小于等于此时小根堆的堆顶元素但是大于大根堆的堆顶元素,那么就放到小根堆up中
            else
            up.push(x);
            //如果大根堆的元素个数比小根堆的元素个数超过了1个以上,那么就把大根堆的堆顶元素放到小根堆中
            if(down.size()>up.size()+1)
            {
                up.push(down.top());
                down.pop();
            }
            //如果小根堆的元素个数比大根堆的元素个数多,那么就把小根堆的堆顶元素放到大根堆中
            if(up.size()>down.size())
            {
                down.push(up.top());
                up.pop();
            }
            //如果读入的整数个数为奇数时,则要输出已读入的整数构成的序列的中位数了
            if(i%2)
            {
                printf("%d ",down.top());
                if(++cnt%10==0)	//如果一行中的输出个数为10个,则需要换行了
                puts("");
            }
        }
        //假设输入的n个数据不足10个,比如n=4,那么只输入了4个数,那么这四个数的输出就一定不会执行for循环中的if语句中的换行。
        //但是输出这4个数据之后应该换行,因为下一组测试数据要来了。
        if(cnt%10)
        puts("");
    }
    return 0;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卷心菜不卷Iris

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值