В силу непонятных причин этот текст просматривается чаще других, размещенных здесь, может быть даже чаще других вместе взятых. Скорее всего, написанное ниже верно, но ничего нового оно не содержит и только из-за него автор не стал бы создавать страницу в интернете. Поэтому, прежде чем продолжить, посмотрите, что еще здесь есть. Это также объяснит выбор языка, на котором написаны примеры.
Предполагается, что сортируемые данные размещаются в массиве записей, каждая из которых содержит ключ (он определяет порядок сортировки) и некоторые сопутствующие данные. Предполагается также наличие функции сравнения двух ключей (оператора "меньше" или "меньше или равно"). Сопутствующие данные никак не влияют на порядок сортировки и могут не рассматриваться. Действительно, можно определить второй массив целых, элементы которого указывают на элементы первого массива, затем сортировать только его:
Чтобы протестировать перечисленные функции можно использовать следующую программу:
В языке Context нет встроенных функций, необходимые функции вывода на консоль должны быть тем или иным образом реализованы и помещены в текст программы. Эти функции могут быть помещены в отдельный файл, включаемый в программу с помощью препроцессора (в DOS-версии - внешнего), но здесь для простоты они помещены непосредственно в текст программы.
Чтобы протестировать какую-либо из функций сортировки, ее текст надо скопировать на место функции Sort, после чего программу нужно откомпилтировать и выполнить.
Сортировка отбором (selection sort)
'Очевидный' алгоритм сортировки - перебором находится наименьший элемент, он
меняется местами с элементом, стоящим на нулевом месте, затем находится наименьший
среди оставшихся и меняется местами с элементом, стоящим на первом месте... Цикл
заканчивается когда будут выбраны все элементы:
Сортировка вставками (insertion sort)
Сортируемый масив просматривается в порядке возрастания номеров и каждый элемент
вставляется в уже просмотренную часть массива так, чтобы сохранить порядок. На
каждом шаге сортировки часть массива уже упорядочена, поэтому для поиска места
вставки можно использовать метод половинного деления:
Пузырьковая сортировка (bubble sort)
Наверное, самый короткий алгоритм сортировки, но и один из самых медленных:
Сортировка слиянием (merge sort)
'Divide-and-conquer' - 'разделяй и властвуй' - исходный массив делится
пополам, каждая половинка упорядочивается, затем производится слияние. Для
сортировки половинок используется та же функция, что и для сортировки всего
массива, функция слияния выделена для экономии места в стеке:
Сходная идея используется в алгоритме распределения/слияния:
Сортировка с помощью кучи (heap sort)
Этот алгоритм почти также эффективен, как сортировка слиянием и он не
требует дополнительной памяти. Но вот назвать его очевидным вряд ли можно...
Алгоритм основан на том, что сортируемому массиву может быть поставлено в соответствие двоичное дерево, каждый узел которого соответствует одному элементу массива с некоторым номером k и этот узел содержит ссылки на два других узла, соответствующих элементам с номерами 2*k+1 и 2*k+2. Корень дерева соответствует элементу с номером 0 (ноль).
Так определенная структура действительно является двоичным деревом и все элементы массива входят в него. Предположим, что это не так и некоторому элементу m не соответствует никакой узел дерева. Тогда и элементу n=(m-1)/2 не соответствует никакой узел дерева (иначе приходим к противоречию, т.к. m=2*n+1 или m=2*n+2). Продолжая рассуждение придем к тому, что элемент с номером 0 (т.е. корень дерева) не входит дерево, опять противоречие.
Далее, допустим, что на некоторый элемент k имеются ссылки из узлов с различными номерами p и q (без ограничения общности q>p, т.е. q=p+r, r>=1):
Противоречие доказывает, что построенная структура является двоичным деревом.
Дальнейшие действия следующие - массив переупорядочивается так, чтобы для любого k выполнялись неравенства:
Затем массив еще раз переупорядочивается, уже по возрастанию номеров. Каждый из этих шагов требует ~N*log(N) операций. 'Очевидная' реализация может быть такой:
Естественно, такой алгоритм работает быстрее и требует меньше памяти, но, наверное, он еще менее очевиден, чем рекурсивный.
Можно заметить, что вложенные циклы (while TRUE do) одинаковы и могут быть вынесены в отдельную функцию:
Быстрая сортировка (quick sort)
Алгоритм основан на разделениии сортируемого массива на части и перестановке элементов
таким образом, чтобы элементы в левой части массива не превосходили элементов в правой.
На каждом шаге выбирается 'средний' элемент массива, в результате ряда перестановок он
занимает свое свое законное место, затем каждая из частей упорядочивается:
В более сложных реализациях алгоритма 'средний' элемент выбирается как средний из трех или случайным образом. Но как минимум, необходимо заменить один из рекурсивных вызовов циклом, это предотвратит разрушение стека:
Сортировка подсчетом (counting sort)
Неуниверсальный алгоритм, использующий дополнительную память и выполняющий
сортировку за время, пропорциональное числу элементов массива. Для его
реализации необходимо взаимно однозначно сопоставить каждому возможному
значению элемента массива целое неотрицательное число и нужно, чтобы
максимальное из этих чисел не превосходило значительно числа элементов
массива. Тогда имеет смысл следующий алгоритм:
Алгоритм может быть использован и в случае, когда ширина диапазона значений элементов массива значительно превосходит число элементов. Очевидно, любой ключ представляется послебовательностью байт. Можно разделить ключ на группы и последовательно упрорядочить массив по каждой из групп. Время сортировки в этом случае пропорционально произведению числа элементов на число групп. Например, если дан массив строк, каждая строка может быть разбита на группы по два символа. Максимальное значение ключа в группе не превосходит 65535 (2**16-1).
Алгоритм эффективен только для очень больших массивов.