edubun
Русский

Сортировка 2: слиянием и быстрая

Чтобы отсортировать миллион значений, сравнивая каждую пару, нужно около 500 миллиардов сравнений; сортировке слиянием нужно около 20 миллионов.

Раздели задачу, реши половины

Способы из прошлого урока работают сразу со всем списком и расплачиваются примерно n² сравнениями. Два способа в этом уроке следуют другому плану, он называется «разделяй и властвуй». Раздели список на меньшие части, отсортируй каждую часть тем же способом, а потом объедини отсортированные части. Деление продолжается, пока в части не останется одно значение или ни одного, а список из одного значения уже отсортирован. Поскольку способ вызывает сам себя на меньших списках, он рекурсивный (рекурсию подробно разбирает урок 6).

Сортировка слиянием: вся работа в слиянии

Сортировка слиянием делит список пополам, сортирует левую половину, сортирует правую половину и сливает две отсортированные половины в одну. Хитрость в слиянии. Два отсортированных списка можно объединить, глядя только на их первые значения. Сравни два первых значения, запиши меньшее и перейди за него. Повторяй, пока один список не опустеет, затем перепиши остаток другого. Слияние [1, 4, 7] и [2, 3, 8] идёт так: 1 (1 и 2), 2 (4 и 2), 3 (4 и 3), 4 (4 и 8), 7 (7 и 8), а затем переписывается 8: 5 сравнений на 6 значений. Слиянию двух списков, в которых всего n значений, никогда не нужно больше n − 1 сравнений.

function merge(left, right) {
  const out = [];
  let i = 0, j = 0;
  while (i < left.length && j < right.length) {
    if (right[j] < left[i]) out.push(right[j++]);
    else out.push(left[i++]);
  }
  return out.concat(left.slice(i), right.slice(j));
}

function mergeSort(a) {
  if (a.length <= 1) return a;          // уже отсортирован
  const mid = Math.floor(a.length / 2);
  return merge(mergeSort(a.slice(0, mid)), mergeSort(a.slice(mid)));
}

Почему это быстро? Представь деление пополам как уровни. Список из 8 значений делится на 2 списка по 4, затем на 4 списка по 2, затем на 8 списков по 1: три уровня, потому что 8 можно поделить пополам 3 раза. На каждом уровне все слияния вместе касаются каждого значения один раз, поэтому уровень стоит не больше примерно n сравнений. При log₂ n уровнях всего выходит около n log₂ n. Для миллиона значений это около 20 миллионов сравнений против примерно 500 миллиардов у способов, которые сравнивают пару за парой. При делении на [38, 27, 43, 3] и [9, 82, 10], как в лаборатории, список [38, 27, 43, 3, 9, 82, 10] требует 14 сравнений; код выше делит его на 3 и 4 и требует 13.

Плата за это память. Слиянию нужно место для записи результата, поэтому сортировка слиянием использует второй список такого же размера, как входной. Сортировка слиянием ещё и устойчива: равные при сравнении значения сохраняют исходный порядок, потому что при равных первых значениях слияние берёт из левого списка. Это важно, когда сортируешь таблицу по одному столбцу за другим, например по городу, а затем по имени.

Загрузка

Перетащи, чтобы повернуть. Колёсико или щипок для масштаба.

Сортировка слиянием закрашивает часть, которая сливается; быстрая сортировка отмечает опорный элемент фиолетовым. Увеличь число значений до 40 и сравни счётчики с сортировкой вставками.

Быстрая сортировка: делим вокруг опорного элемента

Быстрая сортировка делит список по-другому. Она выбирает одно значение, опорный элемент, и переставляет список так, чтобы всё меньшее опорного стояло перед ним, а всё остальное после. Этот шаг называется разбиением. Теперь опорный элемент на своём окончательном месте, и две стороны можно отсортировать по отдельности тем же способом. Потом ничего сливать не нужно, потому что каждое значение слева уже меньше каждого значения справа.

Лаборатория использует распространённый учебный вариант, где опорным элементом каждой части берут последнее значение. На [5, 1, 4, 2, 8] опорный элемент 8 сравнивается с четырьмя другими значениями, все меньше, поэтому 8 остаётся в конце. Следующая часть [5, 1, 4, 2] берёт опорный элемент 2: меньше только 1, так что после двух обменов список становится [1, 2, 4, 5, 8]. Ещё одно сравнение обрабатывает [4, 5]. Итого: 8 сравнений и 2 обмена.

Когда опорный элемент оказывается около середины, каждое разбиение делит задачу пополам, и быстрая сортировка делает около n log₂ n сравнений, как сортировка слиянием. К тому же она работает внутри исходного списка без второй копии. Но плохой опорный элемент вредит. Если список уже отсортирован и опорным всегда берут последнее значение, каждое разбиение отделяет всего одно значение, и быстрая сортировка скатывается примерно к n²/2 сравнениям: 10 для [1, 2, 3, 4, 5], как у сортировки выбором. Настоящие библиотеки избегают этого, выбирая опорный элемент внимательнее, например случайно или как средний из трёх взятых значений.

КонтекстУ обоих способов известно происхождение. Сортировку слиянием предложил Джон фон Нейман в 1945 г., когда проектировались первые компьютеры с хранимой программой. Быструю сортировку придумал Тони Хоар, британский студент Московского государственного университета, в 1959 г. Ему предложили работу над машинным переводом с русского на английский, а поскольку словарь хранился на магнитной ленте, слова каждого предложения сначала нужно было расставить по алфавиту. Его первая идея требовала времени, пропорционального квадрату длины предложения; вторая стала быстрой сортировкой. Он опубликовал её в 1961 г.
СпособСравненияДополнительная память
Сортировка вставкамиобычно около n²/4, в худшем случае около n²/2не нужна; устойчивая
Сортировка слияниемоколо n log₂ n, всегдавторой список из n значений; устойчивая
Быстрая сортировкаобычно около n log₂ n, в худшем случае около n²/2 (плохие опорные элементы)мало; обычные варианты неустойчивы
Может ли сортировка сравнениями обогнать n log₂ n? Нет. Сортировка, которая узнаёт что-то только сравнением пар, должна уметь различать все n! возможных начальных порядков, а у каждого сравнения два исхода, поэтому в худшем случае нужно не меньше log₂(n!) сравнений, а это растёт как n log₂ n. Для 10 значений эта граница равна 22 сравнениям.
разделяй и властвуйРазделить задачу, решить части тем же способом, объединить результаты
слияниеОбъединение двух отсортированных списков: каждый раз берём меньшее из первых значений
сортировка слияниемДелим пополам, сортируем каждую половину, сливаем: около n log₂ n сравнений
опорный элементЗначение, вокруг которого быстрая сортировка делит список
разбиениеПерестановка, после которой меньшие значения стоят перед опорным, а остальные после
быстрая сортировкаРазбиение вокруг опорного элемента, затем сортировка каждой стороны
устойчивая сортировкаРавные значения сохраняют исходный порядок
на местеСортирует внутри исходного списка почти без дополнительной памяти
  1. Сколько сравнений нужно, чтобы слить [1, 4, 7] и [2, 3, 8]?
  2. Сколько сравнений нужно, чтобы слить [1, 2, 3] и [4, 5, 6]?
  3. Список из 16 значений делят пополам, пока в каждой части не останется одно значение. Сколько уровней деления?
  4. Сколько примерно сравнений делает сортировка слиянием на миллионе значений?
    • около 20
    • около 20 миллионов
    • около 500 миллиардов
    • ровно миллион
  5. Где оказывается опорный элемент после разбиения?
    • На своём окончательном месте в отсортированном порядке
    • В начале
    • В конце
    • Где угодно; потом он ещё сдвинется
  6. Быстрая сортировка с последним значением в роли опорного работает на уже отсортированном списке [1, 2, 3, 4, 5]. Сколько сравнений она делает?
  7. Какому способу нужен второй список такого же размера, как входной?
    • Сортировка слиянием
    • Быстрая сортировка
    • Сортировка вставками
  8. Кто предложил сортировку слиянием в 1945 г.?
    • Тони Хоар
    • Джон фон Нейман
    • Ады Лавлейс
    • Евклида
  9. Какая задача привела Тони Хоара к быстрой сортировке?
    • Сортировка слов предложения для словаря на магнитной ленте
    • Сортировка книг в библиотеке
    • Ранжирование веб-страниц
    • Сортировка банковских чеков
  10. Почему сортировку слиянием называют устойчивой?
    • Равные значения сохраняют исходный порядок
    • Она никогда не падает
    • Она всегда занимает одно и то же время
    • Она не использует дополнительную память
  11. Каково наименьшее возможное число сравнений в худшем случае для любой сортировки сравнениями 10 значений (log₂ 10!, округлённый вверх)?
Главное
  • Разделяй и властвуй: раздели, реши части, объедини.
  • Сортировка слиянием: всегда около n log₂ n сравнений, нужна дополнительная память, устойчивая.
  • Быстрая сортировка: разбиение вокруг опорного элемента; в среднем быстрая, около n²/2 при плохих опорных элементах.
  • Миллион значений: около 20 миллионов сравнений вместо 500 миллиардов.
  • Ни одна сортировка сравнениями не обгонит примерно n log₂ n в худшем случае.
Источники курса (11)
  • MIT OpenCourseWare 6.006 Introduction to Algorithms, Spring 2020 (Demaine, Ku, Solomon)
  • lecture notes 1 Introduction, 3 Sorting, 9 Breadth-First Search, 10 Depth-First Search (ocw.mit.edu/courses/6-006-introduction-to-algorithms-spring-2020/pages/lecture-notes) and Fall 2011 lecture 13 BFS notes (ocw.mit.edu/courses/6-006-introduction-to-algorithms-fall-2011)
  • Cormen, Leiserson, Rivest, Stein, Introduction to Algorithms (MIT Press)
  • with the CLRS-based Khan Academy algorithms unit by Thomas Cormen and Devin Balkcom of Dartmouth (khanacademy.org/computing/computer-science/algorithms: binary search, selection sort analysis, merge sort, quicksort overview and analysis)
  • MacTutor History of Mathematics, Al-Khwarizmi (mathshistory.st-andrews.ac.uk/Biographies/Al-Khwarizmi: word algorithm from the Latin title Algoritmi de numero Indorum)
  • D. E. Joyce, Euclid's Elements Book VII Propositions 1 and 2 (mathcs.clarku.edu/~djoyce/elements/bookVII)
  • Wolfram MathWorld, Merge Sort (mathworld.wolfram.com/MergeSort.html: proposed by John von Neumann in 1945, after Knuth 1998 p. 159)
  • Stanford CS "Tony Hoare: Quicksort" (cs.stanford.edu/people/eroberts/courses/soco/projects/2008-09/tony-hoare/quicksort.html: devised while a student at Moscow State University for Russian-English machine translation with a tape dictionary, programmed in Algol 60 and published 1961; the year 1959 from the Wikipedia Quicksort article and its cited sources)
  • Bodleian Libraries, Ada Lovelace (blogs.bodleian.ox.ac.uk/adalovelace: 1843 paper, Note G Bernoulli numbers)
  • Fourmilab text of Menabrea's Sketch with Lovelace's notes (fourmilab.ch/babbage/sketch.html)
  • Seven Bridges of Konigsberg (Euler 1736) and Tower of Hanoi (Edouard Lucas, 1883) checked against the Wikipedia articles and their cited sources.

Проверено: 2026-10-17