Алгоритм быстрой сортировки
QuickSort – это классический алгоритм декомпозиции. Чтобы отсортировать массив, выбирается расщепляющий элемент и производится разбиение: элементы переставляются так, чтобы меньшие элементы оказались по одну сторону, а большие по другую, а затем полученные два подмассива рекурсивно сортируются. Что происходит с элементами, равными расщепляющему значению? В методе Хоара меньшие элементы отправляются влево, а большие вправо, равные элементы могут оказаться в любой из двух сторон.Разработчики алгоритмов давно осознали преимущества и трудности троичного разбиения. Седжвик
[22] на странице 244 замечает: «В идеале, мы хотели бы разместить все равные ключи в файле так, чтобы все ключи с меньшим значением оказались слева, а с большим – справа. К сожалению, до сих пор не изобретен эффективный способ сделать это...» Дейкстра излагает эту проблему, как «Задачу о Датском национальном флаге»: требуется упорядочить последовательность красных, белых и голубых камешков так, чтобы они следовали в порядке цветов на знамени Голландии. Это соответствует разбиению в алгоритме быстрой сортировки, когда меньшие элементы окрашиваются в красный цвет, равные в белый, а большие в голубой. Троичному алгоритму Дейкстры требуется линейное время (каждый элемент рассматривается ровно один раз), однако в оценке времени работы реализующего его кода появляется гораздо больший множитель, чем для кода, реализующего быструю сортировку Хоара.Вегнер
[27] описывает более эффективные троичные схемы разбиения. Бентли и Макклоу [2] предложили троичное разбиение, основанное на следующем инварианте цикла:Основной цикл разбиений содержит два внутренних цикла. Первый внутренний цикл увеличивает индекс
b: он пропускает меньшие элементы, перемещает равные элементы к a, и останавливается, когда встречает больший элемент. Второй цикл, соответственно, уменьшает индекс c: он пропускает большие элементы, перемещает равные к d, и останавливается, когда встречает меньший элемент. Основной цикл затем меняет местами элементы, на которые указывают b и c, увеличивает b и уменьшает c; он останавливается, когда b и c совпадают. (Вегнер предложил тот же инвариант, однако, его коды сложнее). Работа завершается тем, что равные элементы перемещаются из концов в середину массива, при этом уже не нужны сравнения. Для разбиения n-элементного массива этому алгоритму требуется n-1 сравнение.Алгоритм внутренней сортировки анализировали разные авторы, в том числе Хоар
[9], ван Эмдер [26], Кнут [11] и Седжвик [23]. В формулировке точных результатов используются гармонические числа .Теорема 1
[Хоар]. Алгоритм быстрой сортировки с расщепляющими элементами, выбираемыми случайно, сортирует n объектов, среди которых нет равных, в среднем за 2nHn + O(n) » 1.386n lg n сравнений.В стандартных вариантах алгоритма быстрой сортировки расщепляющим элементом служит медиана случайной выборки.
Теорема 2
[Ван Эмден]. Быстрая сортировка с расщеплением вокруг медианы 2t+1 случайно выбранных элементов, сортирует n разных объектов в среднем за 2nHn/(H2t + 2 - Ht + 1) + O(n) сравнений.Увеличивая
t, мы можем приблизить среднее число сравнений к n ln n + O(n).Приведенные выше теоремы имели дело только с средними характеристиками. Чтобы гарантировать разумное время в худших случаях, мы проводим расщепление вокруг истинной медианы, которую можно вычислить за
cn сравнений. (В работе Шенхаге, Патерсона и Пиппендера [20] рассмотрен алгоритм для худшего случая, в котором c = 3; Флойд и Риверст [8] рассматривают алгоритм для среднего времени, у которого c = 3/2.)Рис. 1. Быстрая сортировка и бинарное поисковое дерево
Теорема 3. Быстрая сортировка, расщепляющая вокруг медианы, поиск которой требует cn сравнений, сортирует n элементов за cn ln n + O(n) сравнений.
Доказательство
основано на том, что в рекурсивном дереве около ln n уровней, и на каждом делается не более cn сравнений.Алгоритм быстрой сортировки тесно связан со структурой бинарных деревьев поиска (о структурах данных смотрите у Кнута
[11]). На рисунке 1 показано, как они оба обрабатывают последовательность «31 41 59 26 53». Дерево справа – это стандартное бинарное дерево поиска, сформированное вставкой элементов в порядке ввода. Рекурсивное дерево слева показывает «идеальное разбиение» быстрой сортировки: она проводит разбиение вокруг первого элемента подмассива и оставляет элементы в обоих подмассивах в том же относительном порядке. На первом уровне алгоритм проводит расщепление вокруг значения 31, и выдает левый подмассив «26» и правый подмассив «41 59 53», затем оба рекурсивно сортируются.Рисунок 1 иллюстрирует фундаментальный факт – изоморфизм между быстрой сортировкой и бинарными деревьями поиска. Необведенные расщепляющие значения слева в точности соответствуют внутренним узлам справа, как в горизонтальном, так и в вертикальном направлениях. Длина внутреннего пути дерева поиска равна общему числу сравнений, сделанных обеими структурами. Совпадают не только количества, обе структуры производят один и тот же набор сравнений. Ожидаемая стоимость успешного поиска, по определению, равна длине внутреннего пути, деленной на
n. Объединив это наблюдение с теоремой 1, получаем:Теорема 4
[Хиббард]. Среднее количество сравнений, необходимых для успешного поиска в бинарном дереве поиска, построенном вставкой элементов в случайном порядке, равно 2Hn + O(1) » 1.386 lg n.Аналогичную переформулировку допускает теорема 2: мы можем сократить цену поиска, выбрав в качестве корня поддерева медиану
2t + 1 элементов в поддереве. По аналогии с теоремой 3 сбалансированное дерево сокращает время поиска примерно до lg n.