
- •Квадратичные и субквадратичные алгоритмы
- •Логарифмические и линейные алгоритмы
- •Сравнение времени сортировок
- •Void selectSort(t a[], long size) {
- •Сортировка пузырьком
- •Void bubbleSort(t a[], long size) {
- •Void shakerSort(t a[], long size) {
- •Void insertSort(t a[], long size) {
- •Inline void insertSortGuarded(t a[], long size) {
- •Int increment(long inc[], long size) {
- •Void shellSort(t a[], long size) {
- •44 55 12 42 94 18 06 67 Исходный массив
- •Void downHeap(t a[], long k, long n) {
- •44 55 12 42 // 94 18 06 67 Справа - часть массива,
- •44 55 12 // 67 94 18 06 42 Свойству пирамиды,
- •44 // 94 18 67 55 12 06 42 Остальные элементы
- •94 67 18 44 55 12 06 42 // Иллюстрация 2-й
- •67 55 44 06 42 18 12 // 94 Во внутреннем
- •Void heapSort(t a[], long size) {
- •Void quickSortR(t* a, long n) {
- •Модификации кода и метода
- •Поразрядная сортировка для массивов
- •Сортировка подсчетом
- •Поразрядная сортировка беззнаковых целых чисел
- •Void createCounters(t *data, long *counters, long n) {
- •Void radixPass (short Offset, long n, t *source, t *dest, long *count) {
- •Void radixSort (t* &in, long n) {
- •Поразрядная сортировка целых чисел со знаком
- •Void signedRadixLastPass (short Offset, long n, t *source, t *dest, long *count) {
- •Void signedRadixSort (t* &in, long n) {
- •Формат ieee-754
- •Поразрядная сортировка целых чисел с плавающей точкой
- •Void floatRadixLastPass (short Offset, long n, t *source, t *dest, long *count) {
- •Void floatRadixSort (t* &in, long n) {
- •Эффективность поразрядной сортировки
- •Результаты тестирования
Поразрядная сортировка для массивов
Списки довольно удобны тем, что их легко реорганизовывать, объединять и т.п. Как применить ту же идею для массивов ?
Сортировка подсчетом
Пусть у нас есть массив source из n десятичных цифр ( m = 10 ). Например, source[7] = { 7, 9, 8, 5, 4, 7, 7 }, n=7. Здесь положим const k=1.
Создать массив count из m элементов(счетчиков).
Присвоить count[i] количество элементов source, равных i. Для этого:
проинициализовать count[] нулями,
пройти по source от начала до конца, для каждого числа увеличивая элемент count с соответствующим номером.
for( i=0; i<n; i++) count [ source[i] ]++
В нашем примере count[] = { 0, 0, 0, 0, 1, 1, 0, 3, 1, 1 }
Присвоить count[i] значение, равное сумме всех элементов до данного: count[i] = count[0]+count[1]+...count[i-1]. В нашем примере count[] = { 0, 0, 0, 0, 1, 2, 2, 2, 5, 6 } Эта сумма является количеством чисел исходного массива, меньших i.
Произвести окончательную расстановку.
Для каждого числа source[i] мы знаем, сколько чисел меньше него - это значение хранится в count[ source[i] ]. Таким образом, нам известно окончательное место числа в упорядоченном массиве: если есть K чисел меньше данного, то оно должно стоять на позиции K+1. Осуществляем проход по массиву source слева направо, одновременно заполняя выходной массив dest:
for ( i=0; i<n; i++ ) {
c = source[i];
dest[ count[c] ] = c;
count[c]++; // для повторяющихся чисел
}
Таким образом, число c=source[i] ставится на место count[c]. На этот случай, если числа повторяются в массиве, предусмотрен оператор count[c]++, который увеличивает значение позиции для следующего числа c, если таковое будет.
Циклы занимают (n + m) времени. Столько же требуется памяти.
Итак, мы научились за (n + m) сортировать цифры. А от цифр до строк и чисел - 1 шаг. Пусть у нас в каждом ключе k цифр ( m = 10 ). Аналогично случаю со списками отсортируем их в несколько проходов от младшего разряда к старшему.
Общее количество операций, таким образом, ( k(n+m) ), при используемой дополнительно памяти (n+m). Эта схема допускает небольшую оптимизацию. Заметим, что сортировка по каждому байту состоит из 2 проходов по всему массиву: на первом шаге и на четвертом. Однако, можно создать сразу все массивы count[] (по одному на каждую позицию) за один проход. Неважно, как расположены числа - счетчики не меняются, поэтому это изменение корректно. Таким образом, первый шаг будет выполняться один раз за всю сортировку, а значит, общее количество проходов изменится с 2k на k+1.
Поразрядная сортировка беззнаковых целых чисел
В качестве интересного примера рассмотрим компьютерное представление целых беззнаковых чисел. На хранение каждого из базовых типов выделяется строго определенное количество байт. Как правило, распределение такое:
unsigned char - 1 байт,
unsigned short int - 2 байта,
unsigned long int - 4 байта.
При этом большинство процессоров использует обратный порядок записи: от младшего байта к старшему. Так число типа short int i = 669110 = 0x1A23 непосредственно в памяти хранится так:
Если это число имеет тип long int, то под него выделяется 4 байта: long int i = 669110 = 0x00001A23.
В конце расположены ведущие нули. Таким образом, необходимо провести сортировку от начала числа к концу. Каждый байт может принимать в общем случае 256 значений: m=256.
// Создать счетчики.
// data-сортируемый массив, counters-массив для счетчиков, N-число элементов в data
template<class T>