Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции по алгоритмизации сортировки без элемент....doc
Скачиваний:
14
Добавлен:
05.11.2018
Размер:
449.02 Кб
Скачать

Алгоритм 8. Лексикографической сортировки

Вход. Последовательность A1, A2, , An, где Ai есть k-членный кортеж (ai1, ai2, , aik), в котором aij  целое число между 0 и m-1. (Удобной структурой данных для этой последовательности кортежей является массив размера nk.)

Выход. Последовательность B1, B2, , Bn, представляющая собой перестановку A1, A2, , An, что BiBi+1 при 1 i<n.

Метод. Чтобы поместить k-членный кортеж Ai в некоторый черпак, надо сдвинуть лишь указатель на Ai. Поэтому кортеж Ai можно добавить в черпак за фиксированное время, а не только за время, ограниченное числом k. Для хранения «текущей» последовательности элементов используется очередь Queue. Применяется также массив черпаков Bucket, в котором черпак Bucket [i] представляет собой очередь, предназначенную для хранения тех k-член­ных кортежей, у которых число i стоит в компоненте, рассматриваемой в данный момент.

Программа 8 Лексикографической сортировки массива чисел

// Программа сортирует Length-членные кортежи (списки) слов, состоящих из целых

// чисел от 0 до Size-1 с помощью лексикографической сортировки (в алфавитном

// порядке). Входная матрица array содержит сортируемые кортежи. Выходная

// матрица sorted_array содержит отсортированные кортежи.

// Метод сортировки сортировка вычерпыванием.

#include <stdlib.h>

#include <stdio.h>

#include <io.h>

#define NUM 10 // Число сортируемых кортежей

#define NUM2 NUM+2

#define Size 10 // Число знаков в алфавите

#define Length 6 // Размер кортежа

typedef int *Matrix[NUM]; // Определение типа матрицы, представленной по строкам и

// содержащей массив кортежей array

typedef struct{ // Определение структуры типа очередь для массивов

Matrix name; // Queue и Buckets, используемых при сортировке

int *first; . .// вычерпыванием

int *last;

} List;

List Queue, Bucket[Size]; // Определение очереди Queue и массива очередей Bucket

void main()

{

int array[NUM][Length], sorted_array[NUM][Length];

int values[NUM2], number, indj, indl; // Вспомогательные массив и переменные

//

//** В этом месте должна стоять функция ввода массива array **

//

// Заполнение массива, на элементы которого будут указывать указатели Last и First

// образованных очередей

for (indj=0; indj<NUM2; values[indj]=indj-1, indj++);

// Процедура лексикографической сортировки вычерпыванием

// Заталкивание кортежей в очередь Queue

Queue.last=&values[1];

for (indj=0; indj<NUM; indj++)

{

Queue.first=&values[indj+1];

Queue.name[indj]=array[indj];

}

// Цикл сортировки по r-му элементу кортежей

for (indj=Length-1; indj>=0; indj--)

{

// Предварительное опустошение всех черпаков

for (indl=0; indl<Size; indl++)

{

Bucket[indl].last=&values[1],

Bucket[indl].first=&values[0];

}

// Распределение очереди Queue по черпакам

while (*Queue.last<=*Queue.first) ..// Пока очередь не пуста

{

number=Queue.name[*Queue.last][indj]; // Выбор черпака

// Заталкивание i-го кортежа из очереди Queue в черпак с номером number

*Bucket[number].first++;

Bucket[number].name[*Bucket[number].first]=Queue.name[*Queue.last++];

}

// Подготовка очереди Queue для следующего цикла

Queue.last=&values[1];

Queue.first=&values[0];

// Заталкивание содержимого черпаков Bucket в образовавшемся порядке

// в очередь Queue

for (indl=0; indl<Size; indl++) // Текущий черпак

{

while (*Bucket[indl].last<=*Bucket[indl].first) // Пока черпак не пуст

{

*Queue.first++;

Queue.name[*Queue.first]=Bucket[indl].name[*Bucket[indl].last++];

}

}

}

// Заполнение выходной матрицы sorted_array

for (indl=0; indl<NUM; indl++)

for (indj=0; indj<Length; indj++)

sorted_array[indl][indj]=Queue.name[indl][indj];

}// Конец лексикографической сортировки вычерпыванием

Рассмотрим характерный пример.

Пример 2.1. Рассмотрим лексикографическую сортировку кортежей, показанных в таблице 5, с помощью программы 8. Число знаков в алфавите  10. Размер кортежа  6. Результат сортировки показан в той же таблице в последнем столбце.

Таблица 5. Пример лексикографической сортировки кортежей из 6 знаков

Кортежи слов до сортировки

Отсортированные кортежи слов

1

085997

030478

2

441124

034110

3

030478

039818

4

849111

085997

5

039818

356130

6

367043

367043

7

888653

441124

8

356130

849111

9

034110

888653

10

967012

967012

Теорема 2. Алгоритм 8 лексикографически упорядочивает последовательность, состоящую из n k-членных кортежей, каждая компонента которых является целым числом между 0 и m-1, за время O((m+n)k).

Доказательство. Доказательство проводится по индукции по числу выполнений внешнего цикла (цикла сортировки по r-му элементу кортежей в программе 8). Предположим, что после r выполнений этого цикла кортежи в очереди Queue будут расположены в лексикографическом порядке по их r последним компонентам. (r+1)-е выполнение внешнего цикла упорядочивает множество кортежей по их (r+1)-й с конца компоненте. А в ситуации, когда два кортежа оказались в одном черпаке, первый из них предшествует второму в лексикографическом порядке, определяемом r последними компонентами.

Одно выполнение внешнего цикла алгоритма 8 требует времени O(m+n). Цикл повторяется k раз, и это даёт временную сложность O((m+n)k). 

Теперь распространим сортировку вычерпыванием на кортежи неодинаковой длины, которые будут называться строками (String). Если самая длинная строка имеет длину k, то можно каждую цепочку дополнить специальным символом до длины k и затем применить алгоритм 8. Однако, если длинных цепочек мало, такой подход по двум причинам приведёт к неоправданной неэффективности. Во-первых, при каждом прохождении цикла просматривается каждая строка; во-вторых, каждый черпак анализируется даже в том случае, когда почти все черпаки пусты. Поэтому для сортировки последовательности из n строк различной длины, компонентами («буквами») которых служат числа между 0 и m-1, требуется реализовать более эффективный алгоритм с временной сложностью порядка O(m+l*), где l*=l1+l2++ln, а li длина i-й строки. Алгоритм особенно эффективен в ситуации, когда m~l*~O(n).

Суть этого алгоритма в том, что сначала он располагает строки в порядке убывания длин. Пусть lmax – длина самой длинной строки. Тогда сортировка вычерпыванием применяется lmax раз. Но первое такое применение сортирует только строки длины lmax по последней ко­мпоненте. Второе применение сортирует по (lmax–1)-й компоненте все строки, длина которых не менее lmax–1, и т.д. При этом в общем случае во время каждого отдельного прохождения могут оказаться пустыми большое количество черпаков. Поэтому полезен предварительный шаг, определяющий, какие черпаки будут при данном прохождении непустыми. Список непустых черпаков для каждого прохождения формируется в порядке возрастания номеров черпаков. Это позволяет сцепить непустые черпаки за время, пропорциональное их числу.