Алгоритми · Комп'ютерні науки · Структури даних
📅 Квітень 2026 ⏱ ≈ 14 хв читання 🎯 Початковий–середній · Останнє оновлення: 28 травня 2026 р.

Алгоритми сортування візуально — бульбашкове, злиттям, швидке, пірамідальне, підрахунком і порозрядне

Сортування — найбільш досліджувана задача в комп'ютерних науках. Ефективне сортування лежить в основі баз даних, файлових систем, пошукових систем, алгоритмів стиснення та динамічного програмування. Розуміння, коли застосовувати сортування вставками O(n²) проти сортування злиттям O(n log n) проти сортування підрахунком O(n) — і що означають стабільність та in-place — необхідне для написання продуктивного коду.

1. Нижня межа сортування порівнянням

Сортування порівнянням визначає порядок виключно через порівняння елементів (a < b?). Аргумент дерева рішень: • n! можливих упорядкувань n елементів • Двійкове дерево рішень має висоту ≥ ⌈log₂(n!)⌉ • За Стірлінгом: log₂(n!) ≈ n log₂ n - n log₂ e ≈ Θ(n log n) Отже: БУДЬ-ЯКИЙ алгоритм сортування на основі порівнянь потребує щонайменше Ω(n log n) порівнянь у найгіршому випадку. Сортування злиттям і пірамідальне сортування досягають O(n log n) у найгіршому випадку. Швидке сортування — O(n log n) у середньому, O(n²) у найгіршому випадку. Означення: стабільне сортування зберігає відносний порядок рівних елементів. Приклад: сортування за прізвищем має залишити John Smith перед Jane Smith, якщо вони вже були в такому порядку.

2. Прості сортування O(n²)

Сортування вставками

Найкраще підходить для: майже відсортованих даних, малих масивів (n < 50), потокових даних (сортування в міру надходження елементів). O(n) у найкращому випадку на відсортованому вході.

function insertionSort(arr) {
  for (let i = 1; i < arr.length; i++) {
    const key = arr[i];
    let j = i - 1;
    while (j >= 0 && arr[j] > key) {
      arr[j + 1] = arr[j];
      j--;
    }
    arr[j + 1] = key;
  }
  return arr;
}

Бульбашкове сортування

O(n²) у найгіршому/середньому випадку, O(n) у найкращому. Просте, але рідко використовується в продакшені — сортування вставками має кращі сталі множники для малих n.

Сортування вибором

Знаходить мінімальний елемент і ставить його на початок. Завжди O(n²). Примітне мінімізацією перестановок (лише n перестановок загалом) — корисне, коли записи дорогі (наприклад, флеш-пам'ять).

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

Поділяй і володарюй: розбити навпіл, рекурсивно відсортувати кожну половину, злити дві відсортовані половини.

Рекурентне співвідношення: T(n) = 2T(n/2) + O(n) За основною теоремою (a=2, b=2, f(n)=n, n^log_b(a) = n^1): Випадок 2: T(n) = O(n log n) Властивості: • O(n log n) гарантовано — найкращий, середній, найгірший • Стабільне (рівні елементи зберігають відносний порядок) • НЕ in-place: потребує O(n) допоміжної пам'яті для буфера злиття • Дружні до кешу послідовні шаблони доступу Операція злиття: function merge(arr, left, mid, right): copy arr[left..mid] to L[], arr[mid+1..right] to R[] i=0, j=0, k=left while i < L.length and j < R.length: if L[i] <= R[j]: arr[k++] = L[i++] else: arr[k++] = R[j++] copy remaining elements

Висхідне сортування злиттям уникає накладних витрат на рекурсію, зливаючи серії розміром 1, 2, 4, 8, ... — використовується в Arrays.sort у Java та Timsort у Python (який використовує наявні відсортовані серії для O(n) на майже відсортованому вході).

4. Швидке сортування

Ідея: обрати опорний елемент (pivot); розбити масив так, щоб усі менші елементи були ліворуч, а всі більші — праворуч; рекурсивно обробити кожну сторону. T(n) = T(k) + T(n-1-k) + O(n) де k = ранг опорного елемента Найкращий/середній випадок (опорний близький до медіани, k ≈ n/2): T(n) = 2T(n/2) + O(n) = O(n log n) Найгірший випадок (опорний завжди мінімум або максимум, напр. відсортований вхід, поганий опорний): T(n) = T(n-1) + O(n) = O(n²) Стратегії пом'якшення: • Медіана трьох: порівняти перший, середній, останній елемент; узяти медіану як опорний • Випадковий опорний: випадкова перестановка перед сортуванням або випадковий вибір опорного → очікувано O(n log n) незалежно від входу • Introsort (C++ std::sort): швидке сортування + перехід до пірамідального, якщо глибина рекурсії перевищує 2 log n → гарантовано O(n log n) у найгіршому випадку Розбиття (схема Ломуто): pivot = arr[right] i = left - 1 for j = left to right-1: if arr[j] <= pivot: i++, swap(arr[i], arr[j]) swap(arr[i+1], arr[right]) return i+1 Переваги швидкого сортування: • O(1) додаткової пам'яті (in-place) • Чудова поведінка з кешем (послідовний доступ) • Мала стала: ~у 2× швидше за сортування злиттям на практиці для випадкових даних

5. Пірамідальне сортування

Використовує двійкову max-купу (батько ≥ нащадки). У масиві батько i = ⌊(i-1)/2⌋, нащадки: 2i+1, 2i+2. Алгоритм: 1. Побудувати max-купу з масиву: O(n) (за допомогою buildHeap Флойда, а не n вставок) Почати від останнього не-листка ⌊n/2⌋-1 до 0, просіюючи вниз кожен вузол. 2. Багаторазово видобувати максимум (поміняти корінь з останнім, зменшити купу, просіяти вниз): O(n log n) Операція просіювання вниз: O(log n) — порівняти з нащадками, поміняти, якщо менший Властивості: • O(n log n) у найгіршому випадку — гарантовано, на відміну від швидкого сортування • In-place — O(1) пам'яті • НЕ стабільне (далекосяжні перестановки порушують відносний порядок) • Погана поведінка з кешем (шаблон випадкового доступу по масиву) • На практиці у 2–5× повільніше за швидке сортування через промахи кешу Доведення O(n) для побудови купи: ∑_{k=0}^{⌊log n⌋} ⌈n/2^(k+1)⌉ × k ≤ n ∑_{k=0}^∞ k/2^k = 2n = O(n)

6. Сортування за лінійний час

Уникаючи поелементних порівнянь і використовуючи структуру даних, деякі алгоритми сортують за час O(n) — перемагаючи нижню межу порівнянь Ω(n log n):

Сортування підрахунком — O(n + k)

Для цілих чисел у діапазоні [0, k]. Підрахувати входження, потім обчислити префіксні суми, щоб визначити фінальні позиції. O(n + k) за часом і пам'яттю. Стабільне. Практичне, коли k = O(n).

Порозрядне сортування — O(d·(n + k))

Сортувати спершу за найменш значущою цифрою (LSD-порозрядне сортування), використовуючи стабільне сортування (підрахунком) на кожній з d позицій цифр. Цифри в основі b: d = ⌈log_b(макс. значення)⌉. Обрати b = n для O(n) загалом з b-цифровим поданням. Використовується в сортуванні на GPU, розподілених системах.

Блочне сортування — O(n) у середньому

Для рівномірно розподілених дійсних чисел у [0,1). Створити n блоків, що покривають [0,1); розкидати кожен елемент у його блок; відсортувати кожен блок сортуванням вставками; об'єднати. Очікувано O(n), якщо вхід рівномірний. Найгірший випадок O(n²), якщо всі елементи потрапляють в один блок.

7. Порівняльна таблиця

Алгоритм Найкращий Середній Найгірший Пам'ять Стабільне In-place ───────────────────────────────────────────────────────────────────── Бульбашкове O(n) O(n²) O(n²) O(1) Так Так Вибором O(n²) O(n²) O(n²) O(1) Ні Так Вставками O(n) O(n²) O(n²) O(1) Так Так Злиттям O(n lg n) O(n lg n) O(n lg n) O(n) Так Ні Швидке O(n lg n) O(n lg n) O(n²) O(lg n) Ні Так Пірамідальне O(n lg n) O(n lg n) O(n lg n) O(1) Ні Так Timsort O(n) O(n lg n) O(n lg n) O(n) Так Ні Підрахунком O(n+k) O(n+k) O(n+k) O(k) Так Ні Порозрядне (LSD) O(dn) O(dn) O(dn) O(n+k) Так Ні Блочне O(n) O(n) O(n²) O(n) Так Ні Коли що використовувати: n < 50: сортування вставками (просте, швидке для малих n) Загальне призначення: Timsort (Python, Java) / Introsort (C++) Потрібна стабільність: сортування злиттям / Timsort Обмежена пам'ять: пірамідальне сортування / злиття in-place Цілочисельні ключі: порозрядне / підрахунком Майже відсортоване: сортування вставками / Timsort
💻 Досліджуй алгоритми →