Dynamic Median

题目描述

设计一个数据结构,初始为空,支持以下操作:

(1)增加一个元素,要求在log(n)时间内完成,其中n是该数据结构中当前元素的个数。注意:数据结构中允许有重复的元素。

(2)返回当前元素集合的中位数,要求在常数时间内完成。如果当前元素的个数为偶数,那么返回下中位数(即两个中位数中较小的一个)。

(3)删除中位数,要求在log(n)时间内完成。

输入

输入的第一行是一个自然数T,代表测试数据的组数((1 ≤ T ≤ 600))。每组测试数据的第一行是个自然数N,代表操作的次数,1<=N<=10000。后面的N行中的每行代表一个操作,每次操作首先输入一个单字符代表操作的类型:

I表示插入,后面跟着输入一个正整数(这是唯一带有输入数值的操作)。
Q表示查询,输出当前的中位数(这是唯一产生输出的操作)。
D表示删除当前的中位数。

输入保证是正确的:查询时集合保证不为空(即中位数是存在的),删除时保证集合中有足够可供删除的元素。

输出

每次查询操作Q时输出的中位数,每次输出单独占一行。

样例输入

1

8

I 4

I 3

I 5

Q

D

I 10

I 2

Q

样例输出

4

3

解题思路

​ 用一个大顶堆一个小顶堆即可解决上述问题。如下图所示:

这里写图片描述

​ 大顶堆可以用STL中的优先队列priority_queue实现,模板声明需要带有三个参数:

priority_queue<Type,Container,Functional>

Type为数据类型,Container为保存数据的容器,Functional为元素的比较方式。其中Container必须为用数组实现的容器,如vector,但不能用list,STL里默认用vector,比较方式默认用 operator<,所以如果把后面两个参数缺省的话,优先队列是大顶堆,队首元素最大;STL里面定义了一个仿函数,greater<>,对于基本类型可以用这个仿函数声明小顶堆:

 priority_queue<int, vector<int>, greater<int> > q

vector与greater所在的头文件为

#include<vector>
#include<functional>

​ 对于输入的数据,一半较小的元素保存在大顶堆中,另一半较大的元素保存在小顶堆中,大顶堆的堆顶元素小于等于小顶堆的堆顶元素,两个堆元素的数目相差不超过1,这样对于中位数的获取或者删除操作能够从堆顶元素获得。

​ 算法具体实现:

​ 对于插入操作,判断两个堆是否有一个为空,若为空则需要压入大顶堆,即左边的堆中,如果不为空,则需要把输入值与两个堆顶元素进行比较,如果比小顶堆的元素要小,则压入大顶堆中,如果比小顶堆的元素要大,则压入小顶堆中:

if (maxheap.empty() || minheap.empty())
                {
                    maxheap.push(value);
                }
else
                {
                if (value < minheap.top())
                    maxheap.push(value);
                if (value>=minheap.top())
                    minheap.push(value);
                }

这样操作完成之后,则左边的大顶堆比右边的小顶堆元素数目多1或者右边的元素数目比左边多1,通过以下操作是的大顶堆元素比小顶堆元素多1或者相等:

//进行排列,使得左边大顶堆始终比右边小顶堆最多多一个元素
                if (maxheap.size()> (minheap.size()+ 1))
                {
                    temp_value = maxheap.top();
                    maxheap.pop();
                    minheap.push(temp_value);
                }
                if (minheap.size() > maxheap.size())
                {
                    temp_value = minheap.top();
                    minheap.pop();
                    maxheap.push(temp_value);
                }

​ 按照这样的方式排序之后,中位数即为大顶堆的堆顶元素(如果为元素为偶数的情况下,返回较小的元素,此时仍符合):

printf("%d\n", maxheap.top());

​ 对于删除中位数的操作,在弹出大顶堆的堆顶元素之后,此时大顶堆的元素数目可能比小顶堆的元素数目少1,所以需要重新进行判断,平衡两个堆的元素:

maxheap.pop();
                         if (minheap.size() > maxheap.size())
                         {
                             temp_value = minheap.top();
                             minheap.pop();
                             maxheap.push(temp_value);
                         }

全部代码:

#include<iostream>
#include<string>
#include<string.h>
#include<queue>
#include<functional>
#include<vector>

using namespace std;

int main()
{
    int T;
    int N;
    int temp_value;
    char operation;
    priority_queue<int>maxheap;//大顶堆
    priority_queue<int, vector<int>, greater<int>>minheap;//小顶堆

    scanf("%d", &T);
    while (T--)
    {
        //队列元素清空
        while (!maxheap.empty())
            maxheap.pop();
        while (!minheap.empty())
            minheap.pop();

        scanf("%d", &N);
        while (N--)
        {
            scanf(" %c", &operation);
            //scanf("%d",&operation);//bug,scanf输入字符时care
            int value;
            switch (operation)
            {
            case 'I'://插入操作
            {
                scanf("%d", &value);
                if (maxheap.empty() || minheap.empty())
                {
                    maxheap.push(value);
                }
                else
                {
                if (value < minheap.top())
                    maxheap.push(value);
                if (value>=minheap.top())
                    minheap.push(value);
                }
                //进行排列,使得左边大顶堆始终比右边小顶堆最多多一个元素
                if (maxheap.size()> (minheap.size()+ 1))
                {
                    temp_value = maxheap.top();
                    maxheap.pop();
                    minheap.push(temp_value);
                }
                if (minheap.size() > maxheap.size())
                {
                    temp_value = minheap.top();
                    minheap.pop();
                    maxheap.push(temp_value);
                }
                break;
            }
                //左边大顶堆始终比右边小顶堆多一个元素,直接返回大顶堆元素即可
            case 'Q'://查询操作
            {
                         printf("%d\n", maxheap.top());
                         break;
            }
            case 'D'://删除操作
            {
                         maxheap.pop();
                         if (minheap.size() > maxheap.size())
                         {
                             temp_value = minheap.top();
                             minheap.pop();
                             maxheap.push(temp_value);
                         }
                         break;
            }
            }
        }
    }
    return 0;
}

care

编写程序的过程中有几点需要注意的地方:
1、在进行删除中位数操作之后仍然需要重新对堆进行平衡操作;
2、由于输入元素较多,所以scanf会比cin快很多,在对字符串进行scanf时,需要避免回车输入,若为

scanf("%c", &operation);

则能够把空格作为字符输入,只需要在c%前面加入一个空格即可避免输入回车符号

scanf(" %c", &operation);

嗯,两天A的两道题,超时的原因都是因为用的是cin而不是scanf,scanf对于输入字符的操作有很多需要注意的地方,先挖一个坑。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值