Алгоритми злиття — це родина алгоритмів, які використовують декілька відсортованих списків (масивів) як вхідні дані та створюють єдиний вихідний список (масив), що містить усі елементи списків, розташовані у впорядкованому порядку. Ці алгоритми використовуються як підпрограми в різних алгоритмах сортування, найбільш відомі з них сортування злиттям.

Застосування ред.

 
Сортування злиттям

Алгоритм злиття відіграє важливу роль в алгоритмі сортування злиттям, алгоритмі сортування на основі порівняння. Концептуально алгоритм сортування злиттям складається з двох етапів:

  1. Рекурсивно розділяємо список на підсписки приблизно однакової довжини, до тих пір, доки кожен підсписок буде містити лише один елемент, або у випадку ітеративного (знизу вгору) сортування злиттям розглядається список з n елементів як n підсписків розміром 1(будь-який список довжини 1 можна вважати впорядкованим).
  2. Послідовно об'єднуємо підсписки, щоб створити новий впорядкований підсписок — на кожному кроці ми беремо менший з двох перших елементів підсписків і записуємо його в результуючий список. Лічильники номерів елементів результуючого списку і підсписку, з якого було взято елемент, збільшуємо на 1.
  3. Коли один з підсписків вичерпався, додаємо всі елементи, що залишилися з другого підсписку в результуючий список.
  4. Якщо результуючий список буде містити усі елементи — це впорядкований список.

Алгоритм злиття використовується неодноразово в алгоритмі сортування злиттям.

Приклад сортування злиття наведено на ілюстрації. Починається з невпорядкованого масиву з 7 цілих чисел. Масив розділений на 7 частин; кожна частина містить 1 елемент і впорядковується. Потім відсортовані частини об'єднуються для отримання більших, відсортованих частин, поки не залишиться 1, відсортований масив.

Алгоритм був винайдений Джоном фон Нейманом в 1945 році[1] під час роботи над «Манхеттенським проектом» як засіб обробки великих масивів статистичних даних.

Робота цих алгоритмів зазвичай займає час, пропорційну сумі довжин списків. Ці алгоритми також працюють з кількома рядками, помноженим на сумарну довжину, час від часу, щоб помістити індекс на найменший елемент, що може бути досягнуто за допомогою порядку пріоритету, який заснований на моменті часу O (log n), O (m log n) time, де n — кількість списків, які потрібно з'єднати, а m — сума довжин списків. Коли два списки довжиною m з'єднуються, нижня межа порівняння в гіршому випадку становить 2m — 1.

Об'єднання двох списків ред.

Наведений нижче псевдокод демонструє алгоритм, який реалізує злиття вхідних списків (або пов'язані списки або масиви) А і В у новий список С[2][3]

first() — подає перший елемент списку;

append — додає елемент до списку;

rest() — подає список без першого елементу.

function merge(A,B)
    var list C
    while length(A) > 0 and length(B) > 0
        if first(A) ≤ first(B)
            append first(A) to C
            A = rest(A)
        else
            append first(B) to C
            right = rest(B)
        end if
    while length(A) > 0 
        append first(A) to C
        A = rest(A)
    while length(B) > 0 
        append first(B) to C
        B = rest(B)
    return C

Реалізація алгоритму мовою програмування С ред.

Для роботи алгоритму ми повинні реалізувати наступні операції:

  • Операцію для рекурсивного поділу масиву на групи (метод Sort).
  • Злиття в правильному порядку (метод Merge).
public void Sort(T[] items)
{
    if (items.Length <= 1)
    {
         return;
    }       

    int leftSize = items.Length / 2;
    int rightSize = items.Length - leftSize;
    T[] left = new T[leftSize];
    T[] right = new T[rightSize];
    Array.Copy(items, 0, left, 0, leftSize);
    Array.Copy(items, leftSize, right, 0, rightSize);
    Sort(left);
    Sort(right);
    Merge(items, left, right);
}  

private void Merge(T[] items, T[] left, T[] right)
{
    int leftIndex = 0;
    int rightIndex = 0;
    int targetIndex = 0;
    int remaining = left.Length + right.Length;
    while(remaining > 0)
    {
        if (leftIndex >= left.Length)
        {
            items[targetIndex] = right[rightIndex++];
        }
        else if (rightIndex >= right.Length)
        {
            items[targetIndex] = left[leftIndex++];
        }
        else if (left[leftIndex].CompareTo(right[rightIndex]) < 0)
        {
            items[targetIndex] = left[leftIndex++];
        }
        else
        {
            items[targetIndex] = right[rightIndex++];
        }
 
        targetIndex++;
        remaining--;
    }
}

Варто відзначити, що на відміну від лінійних алгоритмів сортування, сортування злиттям буде ділити і склеювати масив незалежно від того, був він відсортований спочатку чи ні. Тому, незважаючи на те, що в гіршому випадку він відпрацює швидше, ніж лінійний, в кращому випадку його продуктивність буде нижче, ніж у лінійного. Тому сортування злиттям — не найкраще рішення, коли треба впорядкувати частково впорядкований масив.

Паралельне злиття ред.

У мультипрограмуванні масиви відсортованих значень можна ефективно об'єднати, використовуючи задачу пошуку всіх найближчих найменших значень.

Паралельне злиття також може бути реалізоване за допомогою алгоритму поділу за правилом. Цей алгоритм добре працює, коли використовується з швидким послідовним злиттям як базовий випадок для об'єднання малих масивів. Реалізація з використанням Intel Threading Building Blocks (TBB) та бібліотеки паралельних шаблонів Microsoft (PPL), яка працює з багатоядерними процесорами, добре зарекомендувала себе на практиці[4].

Підтримка в мовах програмування ред.

Деякі мови програмування мають вбудовані або бібліотечні функції підтримки для об'єднання упорядкованих колекцій.

C++ ред.

Стандартна бібліотека C++ містить функцію std::merge, яка об'єднує два відсортовані масиви, і std::inplace_merge, яка об'єднує два послідовно відсортованих масиви на місці. Клас std::listмає власний метод merge(), який приєднує до іншого списку. Типи підключених елементів повинні містити оператор менше (<), або ж повинен бути передбачений довільний компаратор.

C++ 17 дозволяє різні політики виконання, а саме послідовну, паралельну і паралельно-непослідовну[5].

Python ред.

Стандартна бібліотека Python (станом на версію 2.6) також має функцію merge()в heapqмодулі, яка приймає кілька відсортованих масивів та об'єднує їх в один[6].

Переваги та недоліки ред.

Переваги ред.

  • працює навіть на структурах даних послідовного доступу;
  • добре поєднується з підкачкою та кешуванням пам'яті;
  • непогано працює в паралельному варіанті: легко розбити завдання між процесорами порівну, але важко зробити так, щоб інші процесори взяли на себе роботу, в разі якщо один процесор затримається;
  • не має «важких» вхідних даних;
  • стійкий — зберігає порядок рівних елементів (що належать одному класу еквівалентності в порівнянні);

Недоліки ред.

  • на «майже відсортованих» масивах працює так само довго, як на хаотичних. Існує варіант сортування злиттям, який працює швидше на частково впорядкованих даних, але він вимагає додаткової пам'яті, в доповненні до тимчасового буферу, який використовується безпосередньо для сортування.
  • вимагає додаткової пам'яті за розміром вихідного масиву.

Див. також ред.

Джерела ред.

Примітки ред.

  1. Knuth, Donald Ervin, 1938- (©1973-©1981). The art of computer programming. Reading, Mass.: Addison-Wesley Pub. Co. ISBN 0-201-03809-9. OCLC 823849. Архів оригіналу за 15 червня 2020. Процитовано 13 листопада 2020.
  2. Mehlhorn, Kurt, 1949- (2008). Algorithms and data structures : the basic toolbox. Berlin: Springer. ISBN 978-3-540-77978-0. OCLC 272306813.
  3. Katajainen, Jyrki; Pasanen, Tomi; Teuhola, Jukka (1996). Practical in-place mergesort. 3 (1): Nordic J. Computing. с. 27—40.
  4. VJ Duvanenko, "Параллельный Merge", д - р Добба Journal, февраль 2011. Архів оригіналу за 24 серпня 2011.
  5. "std:merge". cppreference.com. 2018-01-08. Retrieved 2018-04-28. Архів оригіналу за 11 листопада 2020.
  6. 8.4. heapq — Heap queue algorithm — Python v2.7.5 documentation. Архів оригіналу за 18 жовтня 2012.

Шаблон:2,4,7,9,10,13,20 amelia.ai #BinarySearchAlgorithm