
- •Сортировки
- •Введение
- •Формулировка задачи сортировки
- •Простейшие методы сортировки
- •Алгоритм линейной сортировки (метод прямого выбора)
- •1 Способ 2 способ
- •Алгоритм сортировки обменом (метод "Пузырька")
- •Усовершенствованная "пузырьковая" сортировка
- •"Шейкер" - сортировка
- •Сортировка подсчетом
- •Алгоритм сортировки вставками (метод прямого включения)
- •Размещение путем сравнения и обмена (просеивание)
- •Размещение путем поиска места и вставки
- •Более сложные и более эффективные методы сортировки
- •Алгоритм сортировки Шелла (метод h-сортировки)
- •Обменная сортировка с разделением (сортировка Хоара)
- •Сортировка методом слияний
- •Простое слияние
- •Естественное двухпутевое слияние
- •Рекурсивный алгоритм слияния
- •Слияние списков
- •Алгоритм сортировки бинарными вставками
- •Сортировка с помощью двоичного включения
- •Лексикографическая сортировка
- •Топологическая сортировка
- •Поразрядная сортировка
- •Пирамидальная сортировка
- •Рекурсивная сортировка
- •Сравнительная характеристика методов сортировки
- •Классификация задач с применением сортировок
- •1. Задачи заполнения
- •2. Задачи анализа
- •3. Задачи поиска
- •4. Задачи перестановки
- •Литература
Лексикографическая сортировка
Топологическая сортировка
Топологическая сортировка применяется для элементов, для которых определен частичный порядок, то есть упорядочение задано не на всех, а только на некоторых парах.
Например:
-
В словаре термин определен через другие термины. Обозначим отношением U V, если слово U определено через слово V. Топологическая сортировка означает, что в словаре все слова, участвующие в определении данного слова, находятся раньше его.
-
В программе процедура может содержать вызов других процедур. Обозначим отношением U V, если процедура U вызывает процедуру V. Топологическая сортировка означает, что в программе описание всех вызываемых процедур находятся раньше описания вызывающей процедуры (понятие рекурсии здесь не рассматривается).
Определим частичный порядок на множестве S как отношение частичного порядка между элементами множества. Обозначим отношение порядка символом (читается «предшествует»).
Отношение порядка удовлетворяет следующим трем свойствам:
-
Если x y и у z , то x z (транзитивность).
-
Если x y , то не у х (ассимметричность).
-
Не выполняется x x (иррефлексивность).
Частичную упорядоченность множества S можно представить в виде ориентированного графа, вершины которого соответствуют элементам множества S, а дуги — отношению порядка между соответствующими вершинами (элементами).
Свойства 1 и 2 частичного порядка элементов множества гарантируют отсутствие циклов в ортографе.
Цель топологической сортировки преобразовать частичный порядок в линейный. Свойства (1) и (2) частичного порядка обеспечивают отсутствие циклов. Это и есть то необходимое условие, при котором возможно преобразование к линейному порядку.
Чтобы найти одно из возможных линейных упорядочений, надо выбрать элемент, которому не предшествует никакой другой. Этот элемент помещается в начало списка и исключается из множества S. Оставшееся множество по-прежнему частично упорядочено, применим тот же алгоритм, пока множество S не станет пустым.
Рассмотрим пример топологической сортировки на множестве чисел: S={1, 2, 3, 4, 5, 6, 7, 8, 9, 10}.
Пусть отношение частичного порядка на множестве S задано следующими отношениями элементов:
-
1<2
-
2<4
-
4<6
-
2<10
-
4<8
-
6<3
-
1<3
-
3<5
-
5<8
-
7<5
-
7<9
-
9<4
-
9<10
Удобно проиллюстрировать отношение частичного порядка с помощью графа, где вершины – это элементы множества, а стрелки – отношение порядка.
На графе отсутствуют циклы, что является необходимым условием частично упорядоченного множества.
Выберем
элемент, которому не предшествует
никакой другой – 7. Исключим его из
множества. Выберем следующий элемент,
которому не предшествует никакой другой
– 9. И т.д.
В результате получим линейное упорядочение вида:
Представим каждый элемент множества тремя характеристиками: идентифицирующим ключом, счетчиком предшествующих элементов, множеством последующих элементов. Удобно организовать данные в виде связного списка. Тогда тип выбранных данных можно описать следующим образом:
Type lref = ^leader; указатель на элемент
tref = ^trailer; указатель на последующий элемент
leader = record
key: integer; идентифицирующий ключ
count : integer; счетчик предшествующих элементов
trail : tref; указатель на список последующих элементов
next : lref; указатель на следующий элемент списка
end;
trailer = record
id : lref; указатель на элемент, являющийся последователем
next : tref; указатель на следующий элемент списка
end;
В результате чтения исходных данных, в памяти будет хранится структура данных, представленная на рисунке:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tail |
||||
1 |
key |
2 |
|
4 |
|
6 |
|
10 |
|
8 |
|
3 |
|
5 |
|
7 |
|
9 |
|
|
0 |
count |
1 |
|
2 |
|
1 |
|
2 |
|
2 |
|
2 |
|
2 |
|
0 |
|
1 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Для создания структуры данных нам понадобится функция, которая для каждого элемента w ищет его в списке и устанавливает на него указатель, а если не находит то добавляет в конец списка.
Будем считать, что список имеем голову (head) и хвост (tail):
function L(w: integer): lref;
var h: lref;
begin
h:= head; tail^.key:=w;
while h^.key <> w do h:= h^.next;
if h = tail then
begin
new(tail);
h^.count:= 0;
h^.trail:= nil;
h^.next:= tail;
end;
L:=h
End;
Введем переменную Z, определяющую число элементов в исходном множестве. При выполнении алгоритма Z уменьшается на 1 каждый раз, когда найден и выведен элемент, не имеющий предшественников. В результате работы Z должно стать равным 0, если выведены все элементы. В противном случае множество не является частично упорядоченным.
В программе использован еще один прием: исходное множество реорганизовано таким образом, чтобы в основном списке остались лишь элементы, у которых нет предшественников. Для удобства элементы в этот списка добавляются с головы.
После реорганизации списка, выполненной следующим образом, список будет выглядеть так:
p:= head; head:= nil;
while p<>tail do
begin
q:= p;
p:=p^.next;
if q^.count= 0 then
begin
q^.next:= head; head:= q;
end
end;
|
|
7 |
HEAD |
0 |
|
0 |
|
|
|
|
|
|
|
|
|
Program Toposort;
Type
lref = ^leader;
tref = ^trailer;
leader = record
key, count : integer;
trail : tref;
next : lref;
end;
trailer = record
id : lref;
next : tref;
end;
Var
head, tail, p, q: lref;
t: tref;
z, x, y: integer;
function L(w: integer): lref;
var h: lref;
begin
h:= head; tail^.key:=w;
while h^.key <> w do
h:= h^.next;
if h = tail then
begin
new(tail);
inc(z);
h^.count:= 0;
h^.trail:= nil;
h^.next:= tail;
end;
L:=h
end;
Begin
new(head); tail:= head;
z:= 0;
{создание структуры данных}
read(x);
while x <> 0 do
begin
read(y);
p:=L(x); q:=L(y);
new(t); t^.id:=q;
t^.next:= p^.trail;
p^.trail:= t;
inc(q^.count);
read(x);
end;
{реорганизация списка}
p:= head; head:= nil;
while p<>tail do
begin
q:= p;
p:=p^.next;
if q^.count= 0 then
begin
q^.next:= head;
head:= q;
end
end;
{проход по списку и вывод элементов, с изменением поля Count у последователей и добавлением новых элементов в список}
q:= head;
while q <> nil do
begin
writeln(q^.key);
dec(z);
t:= q^.trail;
q:= q^.next;
while t <> nil do
begin
p:= t^.id;
dec(p^.count);
if p^.count = 0
then
begin
p^.next:=q;
q:= p;
end;
t:= t^.next
end;
end;
if z <> 0 then writeln('Множество не является частично упорядоченным');
end.