어떤 수열을 읽고, 홀수번째 수를 읽을 때 마다, 지금까지 입력받은 값의 중앙값을 출력하는 프로그램을 작성하시오.
예를 들어, 수열이 1, 5, 4, 3, 2 이면, 홀수번째 수는 1번째 수, 3번째 수, 5번째 수이고, 1번째 수를 읽었을 때 중앙값은 1, 3번째 수를 읽었을 때는 4, 5번째 수를 읽었을 때는 3이다.
자료 구조
우선순위 큐
중앙값을 root
로 잡고, 양측으로 두 개의 우선순위 큐
를 가지면 된다. root
보다 작은 값만을 원소로 가지는 큐(q1
)와 root
보다 큰 값만을 원소로 가지는 큐(q2
)이다. 이때 q1
은 최대 힙으로, q2
는 최소 힙으로 만든다. 이렇게 골격을 짰다면, 나머지는 양 측의 큐의 크기를 동일하도록 보장하면서 입력값에 대해 처리해주면 된다. 또한, 각 큐의 top
은 중앙값의 후보가 된다.
먼저, 첫번째 원소가 입력 되었을 경우에는, 무조건 자기 자신이 root
가 된다. 그 이후 입력된 원소마다 다음의 연산을 하면 된다.
입력된 원소를 in
이라고 하면, 우선 q1
과 q2
의 크기 및 모든 경우를 비교하면서 해야할 연산을 결정한다. 그 경우는 각각 다음과 같다.
q1
의 크기가 q2
의 크기보다 작다면~ (q1
에 원소를 삽입해야 함을 의미)
그렇지 않다면~(q2에 원소를 삽입해야 함을 의미)
다음의 연산이 가능한 것은 q1
은 최대 힙, q2
를 최소 힙으로 구성하는 것에 있다. q1
의 값들은 모두 q1
의 top
보다는 작을 것이고, q2
의 값들 역시 q2
의 top
보다는 클 것이다. 중앙값보다 작은 값들 중 가장 큰 값, 중앙값보다 큰 값들 중 가장 작은 값만 알면 된다. 각각의 top
이 곧 다음 연산 시 root
후보가 되는 것이다.
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
int main()
{
int t, n, in, root;
priority_queue<int> q1;
priority_queue<int, vector<int>, greater<>> q2;
vector<int> res;
cin >> t;
while (t--) {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &in);
if (i == 1)
root = in;
else {
if (q1.size() < q2.size()) {
if (q2.top() < in) {
q1.push(root);
root = q2.top();
q2.pop();
q2.push(in);
}
else if (root < in) {
q1.push(root);
root = in;
}
else q1.push(in);
}
else {
if (!q1.empty()) {
if (q1.top() > in) {
q2.push(root);
root = q1.top();
q1.pop();
q1.push(in);
}
else if (root > in) {
q2.push(root);
root = in;
}
else q2.push(in);
}
else {
if (root < in) q2.push(in);
else q1.push(in);
}
}
}
if (i % 2)
res.push_back(root);
}
printf("%d\n", res.size());
for (int i = 0; i < res.size(); i++) {
printf("%d ", res[i]);
if (i % 10 == 9) printf("\n");
}
printf("\n");
res.clear();
q1 = priority_queue<int>();
q2 = priority_queue<int, vector<int>, greater<>>();
}
return 0;
}