Система непересекающихся множеств — структура данных, которая позволяет администрировать множество элементов, разбитое на непересекающиеся подмножества. При этом каждому подмножеству назначается его представитель — элемент этого подмножества. Абстрактная структура данных определяется множеством трех операций {Union, Find, MakeSet}.
Система непересекающихся множеств очень удобна для хранения компонент связности в графах. К примеру, Алгоритму Краскала необходима подобная структура данных для эффективной реализации.
Пусть конечное множество, разбитое на непересекающиеся классы :
Каждому классу назначается представитель . Соответствующая система непересекающихся множеств поддерживает следующие операции:
Тривиальная реализация сохраняет принадлежность элементов из и представителей в индексном массиве. На практике же, чаще используются множества деревьев. Это позволяет существенно сократить время, необходимое для операции Find. При этом представитель записывается в корень дерева, а остальные элементы класса в узлы под ним.
Для ускорения операций Union и Find используются следующие эвристики.
Во время операции Union(r, s) корень более низкого дерева вешается под корень более высокого дерева. Благодаря этому подходу сохраняется балансировка дерева. Глубина каждого поддерева T не может превысить величину . При использовании этой эвристики worst-case-время операции Find уменьшается с до . Для эффективной реализации предлагается сохранять в корне количество узлов в дереве.
Аналогичная предыдущей эвристика, использующая высоту дерева вместо размера.
Однако можно и не тратить дополнительные памяти на сохранение количества узлов в дереве. Достаточно выбирать корень случайным образом — как ни удивительно, такое решение дает на случайных запросах скорость, вполне сравнимую с реализацией, описанной выше. Тем не менее, если имеется много запросов вида «объединить большое множество с маленьким», данная эвристика улучшает матожидание (то есть среднее время работы) всего в два раза, поэтому использовать её без эвристики сжатия путей не рекомендуется.
Используется чтобы ускорить операцию Find(). При каждом новом поиске, все элементы находящиеся на пути от корня до искомого элемента, вешаются под корень дерева. В этом случае операция Find будет работать в среднем α(n), где α — функция, обратная функции Аккермана. Это позволяет значительно ускорить работу, так как α для всех применяемых на практике значений принимает значение, меньшее 5.
const int MAXN = 1000; int p[MAXN], rank[MAXN]; void MakeSet(int x) { p[x] = x; rank[x] = 0; } int Find(int x) { return ( x == p[x] ? x : p[x] = Find(p[x]) ); } void Union(int x, int y) { if ( (x = Find(x)) == (y = Find(y)) ) return; if ( rank[x] < rank[y] ) p[x] = y; else p[y] = x; if ( rank[x] == rank[y] ) ++rank[x]; }
procedure makeset(x:integer); begin a[x]:=x; end; function find(x:integer):integer; begin if a[x]<>x then a[x]:=find(a[x]); find:=a[x]; end; procedure union(x,y:integer); begin x:=find(x); y:=find(y); if x<>y then if random <= 0.5 then a[x]:=y else a[y]:=x; end; begin randomize; end.
Система непересекающихся множеств.