АЛГОРИТМЫ ДЛЯ НАХОЖДЕНИЯ ОБЪЕДИНЕНИЙ
З а д а ч а: даны элементы 1,2, ..., n. Вначале каждый
элемент образует одноэлементное множество. Нужно выполнить
последовательность операций ОБЪЕДИНИТЬ и НАЙТИ. Будем предпо-
лагать, что множества именуются числами от 1 до n. В частнос-
ти, вначале i = { i } (i=1,...n).
1. АЛГОРИТМЫ С ИСПОЛЬЗОВАНИЕМ МАССИВОВ И СПИСКОВ
Для представления множеств используем 6 массивов размера
1 х n с элементами-числами из [1,n].
R[i] - внутреннее имя множества, в которое входит i;
СЛЕД[i] - следующий элемент множества, в которое входит i,
если i - последний элемент, то СЛЕД[i] = 0;
СПИСОК[j] - первый элемент множества с внутренним именем j;
РАЗМЕР[j] - количество элементов множества с внутр. именем j;
ВНУТР_ИМЯ[k] - внутреннее имя множества с внешним именем k;
ВНЕШ_ИМЯ[j] - внешнее имя множества с внутренним именем j.
function НАЙТИ(i);
НАЙТИ := ВНЕШ_ИМЯ[R[i]]
end.
procedure ОБЪЕДИНИТЬ(A,B,C):
begin
1. if РАЗМЕР[ВНУТР_ИМЯ[A]] <= РАЗМЕР[ВНУТР_ИМЯ[B]]
then begin I := ВНУТР_ИМЯ[A]; J:=ВНУТР_ИМЯ[B] end
else begin I := ВНУТР_ИМЯ[B]; J:=ВНУТР_ИМЯ[A] end;
/* I - имя меньшего мн-ва, J - большего мн-ва */
2. E := СПИСОК[I];
3. while E <>0 do /* переименование меньшего мн-ва
begin R[E] := J; LAST := E; E := СЛЕД[E] end;
/* слияние множеств:
4. СЛЕД[LAST] := СПИСОК[J]; СПИСОК[J] := СПИСОК[I];
РАЗМЕР[J] := РАЗМЕР[J] + РАЗМЕР[I];
/* изменение внешнего имени
5. ВНУТР_ИМЯ[C] := J; ВНЕШ_ИМЯ[J] := C
end.
ТЕОРЕМА 1. С помощью приведенных выше алгоритмов последовательность из m операций НАЙТИ и n-1 операций ОБЪЕДИНИТЬ можно выполнить за время
O(MAX(m, nlog(n))).
-------------------------------------------------------------
2. АЛГОРИТМЫ С ИСПОЛЬЗОВАНИЕМ ДЕРЕВЬЕВ (со сжатием путей)
Структуры данных - 4 массива размера 1хn :
ОТЕЦ[i] - отец вершины i в дереве; если i - корень, то 0;
ИМЯ[i] - имя множества элементов в вершинах дерева с
корнем i;
РАЗМЕР[i] - количество элементов во множестве с корнем i;
КОРЕНЬ[j] - корень дерева, элементы которого образуют
множество с именем j.
Инициализация: ОТЕЦ[i]=0, ИМЯ[i]=i, СЧЕТ[i]=1,
КОРЕНЬ[i]=i для всех i из [1,n].
function НАЙТИ(i):
begin
СПИСОК := пустой_список;
v:=i;
while ОТЕЦ[v] <>0 do
begin добавить v в СПИСОК;
v := ОТЕЦ[v]
end; /* v - корень
НАЙТИ ;= ИМЯ[v];
/* изменение отцов вершин из СПИСКА на v:
for all w из СПИСОК do ОТЕЦ[w] := v
end
procedure ОБЪЕДИНИТЬ(A,B,C):
begin
. if РАЗМЕР[КОРЕНЬ[A]] <= РАЗМЕР[КОРЕНЬ[B]]
then begin I := КОРЕНЬ[A]; J :=КОРЕНЬ[B] end
else begin I := КОРЕНЬ[B]; J :=КОРЕНЬ[A] end;
/* I - корень меньшего дерева, J - большего
ОТЕЦ[I] := J;
РАЗМЕР[J] := РАЗМЕР[J] + РАЗМЕР[I];
ИМЯ[J] := C; КОРЕНЬ[C] := J
end
------------------------------------------
Пусть F(0)=1, F(i+1) = 2^F(i). Определим G(n) = min{ k |
F(k) >=n}. G - медленно растущая функция (покажите, что для
любого k и всех больших n G(n)<log(k,n)=loglog...(k раз)logn).
ТЕОРЕМА 2. Пусть с - некоторая константа. Тогда найдется
такая константа с' (зависяшая от с), что алгоритмы со сжатием
путей позволяют выполнять любую последовательность из cn опе-
раций ОБЪЕДИНИТЬ и НАЙТИ не более чем за c'nG(n) шагов.
-----------------------------------------------------------
З а д а ч и.
1. Пусть операция РАСПЕЧАТАТЬ(А) по имени множества вы-
водит на печать список его элементов. Реализуйте эту операцию
для (а) спискового представления множеств и (б) представления
с помощью деревьев. Оцените сложность получившихся процедур.
Как изменить структуры данныхв случае (б), чтобы время распе-
чатки множества зависело только от его размера?
2. Пусть в последовательности операций сначала идут n1
операций ОБЪЕДИНИТЬ, а затем n2 операций НАЙТИ. Доказать, что
алгоритм со сжатием путей выполняет такую последовательность
за время O(n1+n2).