Алгоритм быстрой сортировки был разработан К.A.Р. Хоаром (C.A.R. Hoare) в 1960 году. Этот алгоритм, наверное, еще более известен, чем пузырьковая сортировка. В настоящее время он является самым широко используемым в программировании методом сортировки, что вызвано его крайне положительными характеристиками: это алгоритм класса O(n log(n)) для общего случая, он требует лишь незначительного объема дополнительной памяти, работает с различными типами входных списков и достаточно удобен для реализации. Но, к сожалению, быстрая сортировка имеет и несколько нежелательных характеристик: при его реализации допускается очень много ошибок (простые ошибки в реализации могут остаться незамеченными и при выполнении могут потребовать дополнительного времени), быстродействие в худшем случае составляет O(n(^2^)) и к тому же она неустойчива.
Кроме того, быстрая сортировка наиболее изучена. Со времени выхода в свет первой статьи Хоара многие исследователи изучали быструю сортировку и сформировали значительную базу данных по теоретическому определению времени выполнения, подкрепленную эмпирическими данными. Было предложено немало улучшений базового алгоритма, позволяющих увеличить скорость работы. Некоторые из предложенных улучшений будет рассмотрены в этой главе. При таком богатстве литературных источников по алгоритму быстрой сортировки, если следовать всем рекомендациям, у вас не должно возникнуть проблем с реализацией. (В последней оптимизированной реализации алгоритма использовалось более шести различных справочных пособий по алгоритмам. Причем в одной из них была приведена "оптимизированная" быстрая сортировка, которая была написана так плохо, что при одних и тех же входных данных работала даже медленнее, чем стандартный метод TList.Sort.)
Быстрая сортировка встречается везде. Во всех версиях Delphi, за исключением версии 1, метод TList.Sort реализован на основе алгоритма быстрой сортировки. Метод TStringList.Sort во всех версиях Delphi реализован с помощью быстрой сортировки. В С++ функция qsort из стандартной библиотеки времени выполнения также реализована на базе быстрой сортировки.
Основной алгоритм быстрой сортировки, как и сортировку слиянием, можно отнести к классу "разделяй и властвуй". Он разбивает исходный список на два, а затем для выполнения сортировки рекурсивно вызывает сам себя для каждой части списка. Таким образом, особое внимание в быстрой сортировке нужно уделить процессу разделения. В разбитом списке происходит следующее: выбирается элемент, называемый базовым, относительно которого переставляются элементы в списке. Элементы, значения которых меньше, чем значение базового элемента, переносятся левее базового, а элементы, значения которых больше, чем значение базового элемента, переносятся правее базового. После этого можно сказать, что базовый элемент находится на своем месте в отсортированном списке. Затем выполняется рекурсивный вызов функции быстрой сортировки для левой и правой частей списка (относительно базового элемента). Рекурсивные вызовы прекращаются, когда список, переданный функции сортировки, будет содержать всего один элемент, а, следовательно, весь список оказывается отсортированным.
Таким образом, для выполнения быстрой сортировки необходимо знать два алгоритма более низкого уровня: как выбирать базовый элемент и как наиболее эффективно переставить элементы списка таким образом, чтобы получить два набора элементов: со значениями, меньшими, чем значение базового элемента, и со значениями, большими, чем значение базового элемента.
Начнем с описания алгоритма выбора базового элемента. В идеале следовало бы выбирать средний элемент списка. Затем при разбиении количество элементов в наборе значений, меньших значения базового элемента, будет равно количеству элементов в наборе значений, больших значения базового элемента. Другими словами, при разбиении исходный список был бы разделен на две равные половины. Вычисление среднего элемента списка (или его медианы) представляет собой достаточно сложный процесс, к тому же стандартный алгоритм его определения использует метод разбиения быстрой сортировки, который мы сейчас обсуждаем. Поэтому нам придется отказаться от определения среднего элемента списка.