
Двоичная поразрядная сортировка
Ключ рассматривается как последовательность битов. Устанавливаем указатели на начало и конец таблицы. Двигаемся слева направо от начала в поисках ключа со старшим битом 1. Двигаемся справа налево от конца в поисках ключа со старшим битом 0. Меняем местами эти ключи. Продолжаем встречное движение по данному принципу до встречи. В результате таблица расщепляется на две части. В левой части все ключи имеют старший бит равный 0, в правой - 1. Таким образом, Aj<Aj+i. Затем эта процедура выполняется для второго бита и полученных подпоследовательностей и т.д.
Пример:
\ S
010 101 111 001 011 100
\ ,/
010 101 111 001 011 100
010 011 001 111 101 100
Получили разделение исходного множества ключей на два. В первом множестве собраны ключи со старшим битом, равным 0, во втором - 1. Далее нужно повторить процесс расщепления для каждого множества по второму биту.
Число расщеплений будет равно log2N. Каждый раз будем просматривать N элементов. Таким образом, время сортировки пропорционально N-log2N .
Функция расщепления.
11 Х- указатель на начало таблицы
// 1, г - границы расщепляемого участка // keylen - длина ключей
// nombit - номер бита, по которому происходит расщепление // возвращает индекс самого правого ключа с битом nombit равным нулю int BitPart (char *t, int keylen, int 1, int r, int nombit) { char *lrec, *rrec; // указатели на левую и правую записи соответственно int lb, rb; // биты левой и правой записи с номером nombit
while (К г) { lrec = t + Pkeylen; // смещаемся в t на Pkeylen байт
rrec = t + r*keylen; // смещаемся в t на r*keylen байт
lb = GiveBit (lrec, nombit); rb = GiveBit (rrec, nombit); if (!lb && !rb) l ++; if (!lb && rb) { 1++; r —; } if (lb && !rb) {
l ++; r --;
Change (lrec,rrec); } if (lb && rb) r --;
}
if (lb && rb) return r-1;
else return r; }
Функция сортировки.
// выполняет сортировку участка от l до r таблицы t с длинной ключа // keylen по биту, начиная с nombit void BSort (char *t, int l, int r, int keylen, int nombit) {
int k;
if (!(l < r)) return; // условие завершения прохода для данного nombit
if (nombit / 8 > keylen) return; // условие завершения по количеству nombit
k = BitPart (t, keylen, l, r, nombit);
BSort (t, l, k, keylen, nombit+1);
BSort (t, k+1, r, keylen, nombit+1); }
Обращение к функции BSort. // n – число ключей void BitSort (char *t, int keylen, int n) {
BSort (t, 0, n-1, keylen, 0); }
static unsigned char bits [ ] = {128, 64, 32, 16, 8, 4, 2, 1}; // record – указатель на запись, из которой выделяем один бит int GiveBit (char *record, int nombit) {
int NomByte;
int BitInByte;
NomByte = nombit / 8;
BitInByte = nombit % 8;
return (record [NomByte] & bits [BitInByte]) >> BitInByte; }
Цифровая поразрядная сортировка по принципу «сначала по младшей цифре»
Возьмем в нулевое множество все ключи с младшим битом 0, в множество с индексом 1, ключи с младшим битом 1 и т.д. Затем выполним подсчет ключей в множествах. Таким образом, получим С – массив счетчиков, в котором С [i] – это число ключей со старшей цифрой i. После подсчета ясно, что все ключи, имеющие младшую цифру 0, должны помещаться, начиная с нулевой позиции, ключи с цифрой 1 – начиная с позиции С [0], с цифрой 2 – с позиции С [0] + C [1] и т.д. Разместим ключи в соответствии с описанным правилом. Таким образом, добились разделения Ai<Ai+1. Далее повторим процесс по следующей цифре и т.д.
Рассмотрим на примере ключей в троичной системе счисления.
Дано множество ключей:
102 211 012 020 201 111 121 022
Для троичной системы счисления размерность массива счетчиков будет три. Тогда С0 будет содержать 1 (имеется только один элемент, с 0 в старшем бите – 020), С1 будет содержать 4 (211, 201, 111, 121), и, наконец, С2 содержит 3 (102, 012, 022). Расставим теперь ключи в соответствии с номерами счетчиков 0, 1, 2. Получим
020 211 201 111 121 102 012 022.
Снова повторяем процесс расщепления по следующей цифре.
C0 = 2 (201, 102); С1 = 3 (211, 111, 012); С2 = 3 (020, 121, 022). Расставляем
201 102 211 111 012 020 121 022. Последнее расщепление для последней цифры. С0 = 3 (012, 020, 022); С1 = 3 (102, 111, 121); С2 = 2 (201, 211).
Расставляем в соответствии с номерами 012 020 022 102 111 121 201 211.
Получена окончательная сортированная последовательность ключей.
Можно брать любую систему счисления. Удобно, если цифра – бит. Поэтому обычно используются либо 16-ричная (один бит – цифра), либо 256-ричная (один байт – цифра). Функция, выполняющая сортировку. #define NDIG 256
void DigitalSort(char *t, int N, int KeyLen){ // t - сортируемый массив // N - его длина
// KeyLen - число цифр в сортируемом ключе int *Count; // массив счетчиков
int Dig; // текущая цифра в ключе
int i,j; // переменные циклов
char *b; // буферная область
int *Pos; // позиции расстановки Count=(int *)calloc(NDIG, sizeof(int)); Pos=(int *)calloc(NDIG, sizeof(int)); b=calloc(N,KeyLen); // цикл по всем цифрам в ключе for(i=0; i<KeyLen; i++){ memset(Count,0, NDIG*sizeof(int)); // обнуляем массив счетчиков
for(j=0; j<N; j++){ // для каждого ключа
Dig=*(t+j*KeyLen+KeyLen-i-1); // получаем i-ю цифру j-го ключа Count[Dig]++; // считаем количество ключей с такой i-й цифрой
}
// расчет позиций, откуда будут начинаться записи с соответствующими цифрами Pos[0]=0; for(j=1; j<NDIG; j++){
Pos[j]=Pos[j-1]+Count[j-1]; }
// расстановка, в соответствии с номером позиции for(j=0; j<N; j++){ Dig=*(t+j*KeyLen+KeyLen-i-1); memcpy(b+KeyLen*Pos[Dig]++, t+j*KeyLen, KeyLen); }
memcpy(t,b,KeyLen*N); // копирование буфера обратно в t и по следующей цифре }
free(b); free(Count); free(Pos); }
Сортировки слиянием Слияние означает объединение двух или более упорядоченных таблиц в одну упорядоченную.