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

Материал из testwiki
Версия от 09:38, 19 марта 2025; imported>Железный капут (Бот: откат правок 94.29.126.30 по запросу Well very well)
(разн.) ← Предыдущая версия | Текущая версия (разн.) | Следующая версия → (разн.)
Перейти к навигации Перейти к поиску

Система непересекающихся множеств (Шаблон:Lang-en, или Шаблон:Lang-en2 Шаблон:Lang-en2) — структура данных, которая позволяет администрировать множество элементов, разбитое на непересекающиеся подмножества. При этом каждому подмножеству назначается его представитель — элемент этого подмножества. Абстрактная структура данных определяется множеством трёх операций: {Union,Find,MakeSet}.

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

Определение

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

S=X0X1X2Xk:XiXj=i,j{0,1,,k},ij.

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

  • MakeSet(x): создаёт для элемента x новое подмножество. Назначает этот же элемент представителем созданного подмножества.
  • Union(r,s): объединяет оба подмножества, принадлежащие представителям r и s, и назначает r представителем нового подмножества.
  • Find(x): определяет для xS подмножество, к которому принадлежит элемент, и возвращает его представителя.

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

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

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

Эвристики

Для ускорения операций Шаблон:Math и Шаблон:Math могут быть использованы эвристики Шаблон:Math, Шаблон:Math, Шаблон:Math и сжатие путей.

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

Эвристика Шаблон:Math аналогична Шаблон:Math, но использует высоту дерева вместо размера.

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

Эвристика сжатия путей используется, чтобы ускорить операцию Find(x). При каждом новом поиске все элементы, находящиеся на пути от корня до искомого элемента, вешаются под корень дерева. В этом случае операция Шаблон:Math будет работать в среднем α(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;

См. также

Литература

Ссылки

Шаблон:Структуры данных

Шаблон:Rq