总时间限制: 3000ms 内存限制: 65536kB
描述
设计一个数据结构,初始为空,支持以下操作:
(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
分析
方法:用大根堆维护前一半元素,用小根堆维护后一半元素。
-
本题输入数据量很大,用cin会超时。必须使用scanf/printf
-
需要注意,维护前一半元素的是大根堆(最大的在堆顶,也就是中位数在堆顶),后一半是小根堆
-
关于插入,只需要跟一个堆顶进行比较即可,不需要跟两个堆顶都比较
-
关于两个堆的平衡问题,建议将中位数维持在其中一个堆顶。按题目要求留在前一半即可。此时前一半元素个数最多比后一半元素数多一个
#include <algorithm>
#include <iostream>
#include <queue>
using namespace std;
struct DynamicMedian {
priority_queue<int> little;
priority_queue<int, vector<int>, greater<int>> large;
void insert(int num) {
if (little.empty()) {
little.push(num);
return;
}
if (num >= little.top()) {
large.push(num);
if (little.size() < large.size()) {
little.push(large.top());
large.pop();
}
} else {
little.push(num);
if (little.size() >= large.size() + 2) {
large.push(little.top());
little.pop();
}
}
}
int query() { return little.top(); }
void del() {
little.pop();
if (little.size() < large.size()) {
little.push(large.top());
large.pop();
}
}
};
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
int t = 0;
scanf("%d", &t);
while (t--) {
DynamicMedian dm;
int n = 0;
scanf("%d", &n);
char op;
while (n--) {
scanf("\n%c", &op);
if (op == 'I') {
int num = 0;
scanf("%d", &num);
dm.insert(num);
} else if (op == 'Q') {
printf("%d\n", dm.query());
} else if (op == 'D') {
dm.del();
}
}
}
}