Опубликован: 26.09.2006 | Уровень: специалист | Доступ: платный
Лекция 3:

Разделенные множества

< Лекция 2 || Лекция 3: 123456 || Лекция 4 >

Примеры использования разделенных множеств

Пример 1. Рассмотрим задачу выделения компонент связности неориентированного графа. Напомним, что компонентой связности называется максимальное по включению подмножество вершин графа такое, что любые две его вершины связаны цепью. Полагаем, что вершины графа пронумерованы числами 1, 2 \dts n и каждое ребро представлено парой ( i,
j ) номеров вершин. Предполагаем также, что множество ребер не пусто.

Алгоритм выделения компонент связности неориентированного графа

\formula{
1. \ \t{Создать коллекцию из } n\ \t{синглетонов
множества}\ \{1, 2\dts n\};\\
2. \ \t{Прочитать очередное ребро } (i, j);\\
3. \ \t{Найти имя } a\ \t{подмножества коллекции,
содержащего элемент } i;\\
4. \ \t{Найти имя } b\ \t{подмножества коллекции, содержащего
элемент}\ j;\\
5. \ \t{Если}\ a \ne b,\ \t{то объединить подмножества
с именами } a\ \t{и } b; \\
6. \ \t{Если есть еще непрочитанные ребра,
перейти к п.}\,2,\\
\mbox{}\q\, \t{в противном случае закончить
вычисления.}
}

Очевидно, построенные подмножества коллекции будут представлять искомые компоненты связности. Используя названия основных операций над коллекцией разделенных множеств, представленный выше алгоритм можно записать в следующем виде:

\formula{
1. \ \t For\ i:= 1\ \t to\ n\ \t{do СОЗДАТЬ}\ (i);\\
2. \ \t{Прочитать очередное ребро}\ (i, j); \\
3. \ \t{НАЙТИ}\ (i, a);\\
4. \ \t{НАЙТИ} (j, b);\\
5. \ \t if\ a \ne b\ \t then\ \t{ОБЪЕДИНИТЬ}\ (a,b);\\
6. \ \t{Если есть еще непрочитанные ребра, перейти к п.}\,2,\\
\mbox{}\q\, \t{в противном случае закончить вычисления}.
}

Пример 2. Рассмотрим неориентированный связный граф без петель, ребрам которого приписаны в качестве весов положительные вещественные числа. Требуется построить остовное дерево, накрывающее все вершины графа и имеющее минимальный суммарный вес входящих в него ребер. Итак, пусть заданный граф G имеет множество V вершин, пронумерованных числами 1, 2\dts n, и множество E ребер. Каждому ребру e из множества E поставлена в соответствие пара (N(e), K(e)) его концевых вершин и число C(e) — его вес. Для решения этой задачи были предложены различные алгоритмы. Мы рассмотрим алгоритм, который разработал Крускал.

Алгоритм Крускала

\formula{
1. \ \t{Создать коллекцию из}\ n\ \t{одноэлементных подмножеств}\\
\mbox{}\q\, \t{множества}\ \{1, 2\dts n\};\\
2. \ \t{Создать пустое множество}\ T;\\
3. \ \t{В множестве}\ E\ \t{найти ребро}\ e\ \t{с минимальным весом
и удалить его}\\
\mbox{}\q\, \t{из множества}\ E;\\
4. \ \t{Найти имя}\ a\ \t{подмножества коллекции, содержащего
элемент}\ N(e);\\
5. \ \t{Найти имя}\ b\ \t{подмножества коллекции, содержащего
элемент}\ K(e);\\
6. \ \t{Если}\ a \ne b, \t{то объединить подмножества
с именами}\ a\ \t{и}\ b,\ \t{а ребро}\ e\\
\mbox{}\q\, \t{добавить к множеству}\ T;\\
7. \ \t{Если множество}\ E\ \t{не пусто и}\ |T| <  n - 1,\ \t{перейти к
п.}\,3,\\
\mbox{}\q\, \t{в противном случае закончить вычисления.}
}

Заметим, что в процессе работы алгоритма в множестве T будут находиться ребра, составляющие ациклический подграф исходного графа, являющийся лесом, состоящим из некоторого числа деревьев. Отсутствие циклов гарантируется проверкой "Если a \ne b " в пункте 6 описанного алгоритма. Фактически при a \ne b происходит объединение двух поддеревьев в одно дерево с помощью ребра e, найденного на шаге 3.

Если исходный граф связен, как сказано в постановке задачи, то построенное с помощью такого алгоритма множество T будет, очевидно, представлять дерево, накрывающее все вершины исходного графа. Доказательство того, что суммарный вес входящих в него ребер будет минимальным, можно найти в разделе "Графы".

В алгоритме естественным образом используется структура разделенных множеств. Обратим внимание на операцию поиска в множестве E ребра e с минимальным весом. Эффективность этой операции во многом зависит от выбора структуры данных для хранения множества E. Приемы эффективного выполнения этой операции рассмотрены в разделе "Приоритетные очереди".

Представление разделенных множеств с помощью массива

Пусть U = \{1, 2\dts n\} — множество, из элементов которого будет строиться коллекция разделенных подмножеств. Одним из очевидных способов представления коллекции является представление ее с помощью массива. При таком способе для каждого элемента i в соответствующей ( i -й) ячейке массива помещаем имя (канонический элемент) того подмножества, которому принадлежит элемент i. Если элемент i не принадлежит ни одному из подмножеств коллекции, то в i -ю ячейку записываем 0.

Реализация операций с помощью массива

Обозначим через f массив длины n, с помощью которого будем представлять коллекцию. Пустая коллекция представляется массивом, заполненным нулями.

Операция СОЗДАТЬ ( x ) осуществляется записью элемента x в ячейку с номером x. Время выполнения операции — O(1).

Операция ОБЪЕДИНИТЬ ( x,y ) осуществляется следующим образом. Просматриваются элементы массива f, и в те ячейки, в которых было записано имя x, заносится новое имя — y. Следовательно, именем вновь образованного подмножества будет y, а x перестанет быть именем какого-либо подмножества. Очевидно, время выполнения этой операции — O(n).

Операция НАЙТИ ( x,
y ) выдает в качестве y содержимое элемента с номером x в массиве f. Время выполнения операции — O(1).

При такой реализации разделенных множеств, очевидно, что время выполнения m произвольных операций, среди которых O(n) операций ОБЪЕДИНИТЬ, есть величина O(m\cdot n).

< Лекция 2 || Лекция 3: 123456 || Лекция 4 >
Антон Сиротинкин
Антон Сиротинкин

на стр 6, лекции 3, Очевидно "Ck <= модуль(Gk(е))*b(k+1)" (1) - , подскажите что значит "модуль" и почему это очевидно...
 

Дмитрий Степаненко
Дмитрий Степаненко
Россия
Эдуард Санин
Эдуард Санин
Украина, Харьков, ХАИ