Знайти медіану - 2#4074

Знайти медіану - 2 (Розбір)

Промоделюємо розв'язання задачі за допомогою лінійного масиву. На кожному кроці будемо зберігати в ньому числа, що надходять, у відсортованому вигляді. Позицію для вставки нового елемента будемо знаходити за допомогою бінарного пошуку. Однак сама операція вставки (наприклад, якщо масив реалізований з використанням структури даних ) виконується за лінійний час. Таким чином, загальне рішення з часовою складністю 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++;
}