Промоделюємо розв'язання задачі за допомогою лінійного масиву. На кожному кроці будемо зберігати в ньому числа, що надходять, у відсортованому вигляді. Позицію для вставки нового елемента будемо знаходити за допомогою бінарного пошуку. Однак сама операція вставки (наприклад, якщо масив реалізований з використанням структури даних ) виконується за лінійний час. Таким чином, загальне рішення з часовою складністю O(n^2) не вкладеться в обмеження за часом.
Оголосимо дві купи: одну — для зберігання мінімуму (-купа), іншу — для зберігання максимуму (-купа). Організуємо їхню роботу так, щоб виконувалися такі умови:
будь-який елемент -купи був меншим за будь-який елемент -купи (менші числа зберігаються в -купі, а більші в -купі);
розміри куп були рівні. Якщо загальна кількість елементів непарна, -купа повинна містити на один елемент більше, ніж -купа.
При додаванні нового елемента поміщаємо його в -купу, якщо він не менший за мінімальний елемент цієї купи; в іншому випадку — в -купу. Після цього виконуємо балансування куп, щоб зберегти зазначені вище властивості:
Якщо розмір -купи перевищує розмір -купи, переносимо найбільший елемент з -купи в -купу.
Якщо розмір -купи більш ніж на один елемент перевищує розмір -купи, переносимо найменший елемент з -купи в -купу.
Медіана поточної послідовності визначається наступним чином:
якщо кількість чисел непарна, медіаною є найменший елемент -купи;
якщо кількість чисел парна, медіана дорівнює середньому арифметичному найменшого елемента -купи та найбільшого елемента -купи.
Вставка елемента в купу та подальше балансування виконуються за час O(log_2n). Таким чином, загальний час роботи алгоритму становить O(nlog_2n).
Приклад
Розглянемо процес обчислення медіани при послідовному надходженні чисел. Для наочності будемо вважати, що після додавання кожного нового числа послідовність впорядковується. Під кожною послідовністю вказано відповідне значення медіани.
Після обробки всіх елементів прикладу стан куп буде наступним:
Оскільки кількість елементів непарна, медіана дорівнює найменшому елементу -купи, тобто .
Нехай наступним надходить число . Оскільки воно не менше за мінімальний елемент -купи, додамо його в -купу. Після цього розмір -купи виявиться на два елементи більшим за розмір -купи, тому необхідно виконати балансування: перенести найменший елемент -купи () в -купу. Іншими словами, видаляємо з -купи і додаємо його в -купу.
Тепер загальна кількість елементів у масиві парна, тому медіана визначається як середнє арифметичне найменшого елемента -купи () та найбільшого елемента -купи (), тобто (35 + 4) / 2 = 19. У задачі потрібно виводити лише цілу частину медіани.
Реалізація алгоритму
Оголосимо -купу mMinHeap та -купу mMaxHeap.
priority_queue<int, vector<int>, greater<int> > mMinHeap;
priority_queue<int, vector<int>, less<int> > mMaxHeap;Ініціалізуємо купи. У змінній будемо зберігати кількість чисел, що надійшли на вхід. Для зручності ініціалізуємо купи фіктивними елементами.
mMaxHeap.push(-MAX); mMinHeap.push(MAX);
c = 1;Обробляємо чергове число.
while(scanf("%d",&n) == 1)
{ Залежно від значення поміщаємо його в одну з куп.
if (n >= mMinHeap.top()) mMinHeap.push(n);
else mMaxHeap.push(n);Якщо розмір -купи перевищує розмір -купи або розмір -купи більш ніж на один елемент перевищує розмір -купи, виконуємо балансування.
if(mMaxHeap.size() > mMinHeap.size())
{ Найбільший елемент -купи переносимо в -купу.
mMinHeap.push(mMaxHeap.top());
mMaxHeap.pop();
} else
if(mMaxHeap.size() + 1 < mMinHeap.size())
{ Найменший елемент -купи переносимо в -купу.
mMaxHeap.push(mMinHeap.top());
mMinHeap.pop();
}Залежно від парності кількості елементів у масиві виводимо значення медіани.
if (c % 2) printf("%d\n",mMinHeap.top());
else printf("%d\n",(mMinHeap.top() + mMaxHeap.top())/2);
c++;
}