Это не официальный сайт wikipedia.org 01.01.2023

Система непересекающихся множеств — Википедия

Система непересекающихся множеств

Система непересекающихся множеств (англ. disjoint-set, или union–find data structure) — структура данных, которая позволяет администрировать множество элементов, разбитое на непересекающиеся подмножества. При этом каждому подмножеству назначается его представитель — элемент этого подмножества. Абстрактная структура данных определяется множеством трёх операций: { U n i o n , F i n d , M a k e S e t } .

Применяется для хранения компонент связности в графах, в частности, алгоритму Краскала необходима подобная структура данных для эффективной реализации.

ОпределениеПравить

Пусть S   конечное множество, разбитое на непересекающиеся подмножества (классы) X i  :

S = X 0 X 1 X 2 X k : X i X j = i , j { 0 , 1 , , k } , i j  .

Каждому подмножеству X i   назначается представитель r i X i  . Соответствующая система непересекающихся множеств поддерживает следующие операции:

  • M a k e S e t ( x )  : создаёт для элемента x   новое подмножество. Назначает этот же элемент представителем созданного подмножества.
  • U n i o n ( r , s )  : объединяет оба подмножества, принадлежащие представителям r   и s  , и назначает r   представителем нового подмножества.
  • F i n d ( x )  : определяет для x S   подмножество, к которому принадлежит элемент, и возвращает его представителя.

Алгоритмическая реализацияПравить

Тривиальная реализация сохраняет принадлежность элементов из S   и представителей r i   в индексном массиве. На практике же чаще используются множества деревьев. Это позволяет существенно сократить время, необходимое для операции Find. При этом представитель записывается в корень дерева, а остальные элементы класса в узлы под ним.

  • U n i o n ( r , s )  : вешает корень более низкого дерева под корень более высокого дерева. Если при этом r   становится потомком s  , оба узла меняются местами.
  • F i n d ( x )  : проходит путь от x   до корня дерева и возвращает его (корень в данном случае является представителем).

ЭвристикиПравить

Для ускорения операций Union и Find могут быть использованы эвристики Union-By-Size, Union-By-Height, Random-Union и сжатие путей.

В эвристике Union-By-Size во время операции U n i o n ( r , s )   корень меньшего дерева вешается под корень большего дерева. Благодаря этому подходу сохраняется балансировка дерева. Глубина каждого поддерева T   не может превысить величину log | T |  . При использовании этой эвристики время операции Find в худшем случае увеличивается с O ( log n )   до O ( n )  . Для эффективной реализации предлагается сохранять в корне количество узлов в дереве.

Эвристика Union-By-Height аналогична Union-By-Size, но использует высоту дерева вместо размера.

В эвристике Random-Union используется тот факт, что можно не тратить дополнительные O ( n )   памяти на сохранение количества узлов в дереве: достаточно выбирать корень случайным образом — такое решение даёт на случайных запросах скорость, вполне сравнимую с другими реализациями. Тем не менее, если имеется много запросов вида «объединить большое множество с маленьким», данная эвристика улучшает матожидание (то есть среднее время работы) всего в два раза, поэтому использовать её без эвристики сжатия путей не рекомендуется.

Эвристика сжатия путей используется, чтобы ускорить операцию F i n d ( x )  . При каждом новом поиске все элементы, находящиеся на пути от корня до искомого элемента, вешаются под корень дерева. В этом случае операция Find будет работать в среднем α ( n )  , где α   — функция, обратная функции Аккермана. Это позволяет значительно ускорить работу, так как α   для всех применяемых на практике значений принимает значение, меньшее 5.

Пример реализацииПравить

Реализация на C++:

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];
    }
}

Реализация на Free Pascal:

const MAX_N = 1000;

var Parent , Rank : array [ 1 .. MAX_N ] of LongInt;

procedure swap ( var x , y : LongInt );
  var tmp : LongInt;
begin
  tmp := x; 
  x := y; 
  y := tmp;
end;

procedure MakeSet ( x : LongInt ) ;
begin
  Parent[x] := x;
  Rank[x] := 0;
end;

function Find ( x : LongInt ) : LongInt;
begin
  if ( Parent[x] <> x ) then
    Parent[x] := Find ( Parent[x] );
  Exit ( Parent[x] );
end;

procedure Union ( x , y : LongInt );
begin
  x := Find ( x );
  y := Find ( y );
  if ( x = y ) then exit();
  if ( Rank[x] < Rank[y] ) then swap ( x , y );
  
  Parent[y] := x;
  if ( Rank[x] = Rank[y] ) then
    inc ( Rank[x] );
end;

См. такжеПравить

ЛитератураПравить

СсылкиПравить