Структура данных с непересекающимся множеством
Непересекающееся множество/Объединение-найти лес | |||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Тип | многопутевое дерево | ||||||||||||||||||||
Изобретенный | 1964 | ||||||||||||||||||||
Изобретён | Бернард А. Галлер и Майкл Дж. Фишер | ||||||||||||||||||||
|
В информатике структура данных непересекающихся наборов , также называемая структурой данных объединения-поиска или набором поиска-слияния , представляет собой структуру данных , которая хранит коллекцию непересекающихся (непересекающихся) наборов . Аналогично, он сохраняет разделение набора на непересекающиеся подмножества . Он предоставляет операции для добавления новых наборов, слияния наборов (замены их объединением ) и поиска репрезентативного члена набора. Последняя операция позволяет эффективно выяснить, находятся ли какие-либо два элемента в одном или разных наборах.
Хотя существует несколько способов реализации структур данных с непересекающимися множествами, на практике их часто отождествляют с конкретной реализацией, называемой лесом с непересекающимися множествами . Это специализированный тип леса , который выполняет объединения и находит почти постоянное амортизированное время . Для выполнения последовательности m операций сложения, объединения или поиска в лесу с непересекающимися множествами с n узлами требуется общее время O ( m α( n )) , где α( n ) — крайне медленно растущая обратная функция Аккермана . Непересекающиеся леса не гарантируют такую производительность для каждой операции. Отдельные операции объединения и поиска могут занимать больше времени, чем постоянное время α( n ) , но каждая операция заставляет лес с непересекающимися множествами корректироваться так, чтобы последующие операции выполнялись быстрее. Непересекающиеся леса являются асимптотически оптимальными и практически эффективными.
Структуры данных с непересекающимися множествами играют ключевую роль в алгоритме Краскала для поиска минимального остовного дерева графа. Важность минимальных связующих деревьев означает, что структуры данных с непересекающимися множествами лежат в основе широкого спектра алгоритмов. Кроме того, структуры данных с непересекающимися множествами также находят применение в символьных вычислениях, а также в компиляторах, особенно для решения задач распределения регистров .
История
[ редактировать ]Непересекающиеся леса были впервые описаны Бернардом А. Галлером и Майклом Дж. Фишером в 1964 году. [2] В 1973 году их временная сложность была ограничена , повторный логарифм , Хопкрофт и Ульман . [3] В 1975 году Роберт Тарьян первым доказал ( обратная функция Аккермана ) верхняя граница временной сложности алгоритма. [4] Он также доказал, что это жестко. В 1979 году он показал, что это нижняя граница для определенного класса алгоритмов, включающих структуру Галлера-Фишера. [5] В 1989 году Фредман и Сакс показали, что (амортизированные) слова биты должны быть доступны любой структуре данных с непересекающимися наборами для каждой операции, [6] тем самым доказав оптимальность структуры данных в этой модели.
В 1991 году Галил и Итальяно опубликовали обзор структур данных для непересекающихся множеств. [7]
В 1994 году Ричард Дж. Андерсон и Хизер Уолл описали распараллеленную версию Union-Find, которую никогда не нужно блокировать. [8]
В 2007 году Сильвен Коншон и Жан-Кристоф Филлиатр разработали полустойкую версию структуры данных леса с непересекающимся множеством и формализовали ее корректность с помощью помощника по доказательству Coq . [9] «Полупостоянный» означает, что предыдущие версии структуры эффективно сохраняются, но доступ к предыдущим версиям структуры данных делает более поздние версии недействительными. Их самая быстрая реализация обеспечивает почти такую же эффективность, как и непостоянный алгоритм. Они не выполняют анализ сложности.
Также были рассмотрены варианты структур данных с непересекающимися множествами, обеспечивающие лучшую производительность при решении ограниченного класса задач. Габоу и Тарьян показали, что если возможные объединения определенным образом ограничены, то возможен алгоритм с действительно линейным временем. [10]
Представительство
[ редактировать ]Каждый узел в лесу с непересекающимися множествами состоит из указателя и некоторой вспомогательной информации: размера или ранга (но не того и другого). Указатели используются для создания родительских деревьев указателей , где каждый узел, не являющийся корнем дерева, указывает на своего родителя. Чтобы отличить корневые узлы от других, их родительские указатели имеют недопустимые значения, например циклическую ссылку на узел или контрольное значение. Каждое дерево представляет собой набор, хранящийся в лесу, причем члены набора являются узлами дерева. Корневые узлы предоставляют представителей множества: два узла находятся в одном наборе тогда и только тогда, когда корни деревьев, содержащих эти узлы, равны.
Узлы в лесу можно хранить любым удобным для приложения способом, но общепринятым методом является их хранение в массиве. В этом случае родители могут быть указаны по индексу их массива. Для каждой записи массива требуется Θ(log n ) бит памяти для родительского указателя. Для остальной части записи требуется сопоставимый или меньший объем памяти, поэтому количество битов, необходимых для хранения леса, равно Θ( n log n ) . Если реализация использует узлы фиксированного размера (тем самым ограничивая максимальный размер леса, который может быть сохранен), то необходимое хранилище является линейным по n .
Операции
[ редактировать ]Структуры данных с непересекающимися наборами поддерживают три операции: создание нового набора, содержащего новый элемент; Нахождение представителя множества, содержащего заданный элемент; и Объединение двух наборов.
Делаем новые наборы
[ редактировать ]The MakeSet
операция добавляет новый элемент в новый набор, содержащий только новый элемент, и новый набор добавляется в структуру данных. Если вместо этого структура данных рассматривается как часть набора, то MakeSet
операция увеличивает набор за счет добавления нового элемента и расширяет существующий раздел, помещая новый элемент в новое подмножество, содержащее только новый элемент.
В бессвязном лесу, MakeSet
инициализирует родительский указатель узла и его размер или ранг. Если корень представлен узлом, указывающим на себя, то добавление элемента можно описать с помощью следующего псевдокода:
function MakeSet(x) is if x is not already in the forest then x.parent := x x.size := 1 // if nodes store size x.rank := 0 // if nodes store rank end if end function
Эта операция имеет постоянную временную сложность. В частности, инициализация Лес с непересекающимся множеством с n узлами требует O ( n ) время.
Отсутствие родительского узла, назначенного узлу, означает, что узел отсутствует в лесу.
На практике, MakeSet
должна предшествовать операция, выделяющая память для хранения x . Поскольку выделение памяти является амортизированной операцией с постоянным временем, как и в случае с хорошей реализацией динамического массива , оно не меняет асимптотическую производительность леса случайного набора.
Нахождение представителей множества
[ редактировать ]The Find
операция следует по цепочке родительских указателей от указанного узла запроса x до тех пор, пока не достигнет корневого элемента. Этот корневой элемент представляет набор, к которому принадлежит x , и может быть самим x . Find
возвращает корневой элемент, которого он достиг.
Выполнение Find
операция представляет собой важную возможность для улучшения леса. Время в Find
операция тратится на поиск родительских указателей, поэтому более плоское дерево приводит к более быстрому Find
операции. Когда Find
выполняется, нет более быстрого способа добраться до корня, чем последовательное следование каждому родительскому указателю. Однако родительские указатели, посещенные во время этого поиска, могут быть обновлены, чтобы они указывали ближе к корню. Поскольку каждый элемент, посещаемый на пути к корню, является частью одного и того же набора, это не меняет наборы, хранящиеся в лесу. Но это делает будущее Find
операции выполняются быстрее не только для узлов между узлом запроса и корнем, но и для их потомков. Это обновление является важной частью амортизированной гарантии производительности непересекающегося леса.
Существует несколько алгоритмов Find
которые достигают асимптотически оптимальной временной сложности. Одно семейство алгоритмов, известное как сжатие пути , заставляет каждый узел между узлом запроса и корнем указывать на корень. Сжатие пути можно реализовать с помощью простой рекурсии следующим образом:
function Find(x) is if x.parent ≠ x then x.parent := Find(x.parent) return x.parent else return x end if end function
Эта реализация делает два прохода: один вверх по дереву и один назад. Для хранения пути от узла запроса до корня требуется достаточно оперативной памяти (в приведенном выше псевдокоде путь неявно представлен с помощью стека вызовов). Это можно уменьшить до постоянного объема памяти, выполнив оба прохода в одном направлении. Реализация постоянной памяти дважды проходит от узла запроса к корню: один раз для поиска корня и один раз для обновления указателей:
function Find(x) is root := x while root.parent ≠ root do root := root.parent end while while x.parent ≠ root do parent := x.parent x.parent := root x := parent end while return root end function
Тарьян и Ван Леувен также разработали однопроходной метод. Find
алгоритмы, которые сохраняют ту же сложность в худшем случае, но более эффективны на практике. [4] Это называется разделением пути и разделением пути пополам. Оба они обновляют родительские указатели узлов на пути между узлом запроса и корнем. При разделении пути каждый родительский указатель на этом пути заменяется указателем на прародителя узла:
function Find(x) is while x.parent ≠ x do (x, x.parent) := (x.parent, x.parent.parent) end while return x end function
Уполовинивание пути работает аналогично, но заменяет только все остальные родительские указатели:
function Find(x) is while x.parent ≠ x do x.parent := x.parent.parent x := x.parent end while return x end function
Объединение двух наборов
[ редактировать ]Операция Union(x, y)
заменяет набор, содержащий x , и набор, содержащий y , их объединением. Union
первое использование Find
определить корни деревьев, содержащих x и y . Если корни одинаковые, то больше делать нечего. В противном случае два дерева необходимо объединить. Это делается либо установкой родительского указателя корня x на y , либо установкой родительского указателя корня y на x .
Выбор того, какой узел станет родительским, влияет на сложность будущих операций с деревом. Если это сделать небрежно, деревья могут стать чрезмерно высокими. Например, предположим, что Union
всегда делал дерево, содержащее x, поддеревом дерева, содержащего y . Начните с леса, который только что был инициализирован элементами. и выполнить Union(1, 2)
, Union(2, 3)
, ..., Union(n - 1, n)
. Результирующий лес содержит одно дерево с корнем n , а путь от 1 до n проходит через каждый узел дерева. Для этого леса самое время бежать Find(1)
О ( п ) .
В эффективной реализации высота дерева контролируется с помощью объединения по размеру или объединения по рангу . Оба из них требуют, чтобы узел хранил информацию, помимо только его родительского указателя. Эта информация используется для принятия решения о том, какой корень станет новым родителем. Обе стратегии гарантируют, что деревья не станут слишком глубокими.
Объединение по размеру
[ редактировать ]В случае объединения по размеру узел сохраняет свой размер, который представляет собой просто количество его потомков (включая сам узел). Когда деревья с корнями x и y объединяются, родительским становится узел с большим количеством потомков. Если два узла имеют одинаковое количество потомков, то любой из них может стать родительским. В обоих случаях размер нового родительского узла устанавливается равным новому общему количеству его потомков.
function Union(x, y) is // Replace nodes by roots x := Find(x) y := Find(y) if x = y then return // x and y are already in the same set end if // If necessary, swap variables to ensure that // x has at least as many descendants as y if x.size < y.size then (x, y) := (y, x) end if // Make x the new root y.parent := x // Update the size of x x.size := x.size + y.size end function
Количество бит, необходимое для хранения размера, очевидно, равно количеству бит, необходимому для хранения n . Это добавляет постоянный коэффициент к требуемому запасу леса.
Союз по рангу
[ редактировать ]При объединении по рангу узел сохраняет свой ранг , который является верхней границей его высоты. Когда узел инициализируется, его ранг устанавливается равным нулю. Чтобы объединить деревья с корнями x и y , сначала сравните их ранги. Если ранги разные, то родительским деревом становится большее дерево рангов, а ранги x и y не изменяются. Если ранги одинаковы, то любой из них может стать родителем, но ранг нового родителя увеличивается на единицу. Хотя ранг узла явно связан с его высотой, хранение рангов более эффективно, чем хранение высот. Высота узла может меняться во время Find
операция, поэтому сохранение рангов позволяет избежать дополнительных усилий по поддержанию правильной высоты. В псевдокоде объединение по рангу выглядит так:
function Union(x, y) is // Replace nodes by roots x := Find(x) y := Find(y) if x = y then return // x and y are already in the same set end if // If necessary, rename variables to ensure that // x has rank at least as large as that of y if x.rank < y.rank then (x, y) := (y, x) end if // Make x the new root y.parent := x // If necessary, increment the rank of x if x.rank = y.rank then x.rank := x.rank + 1 end if end function
Можно показать, что каждый узел имеет ранг или меньше. [11] Следовательно, каждый ранг может храниться в O (log log n ) битах, а все ранги могут храниться в O ( n log log n ) битах. Это делает ряды асимптотически незначительной частью размера леса.
Из приведенных выше реализаций ясно, что размер и ранг узла не имеют значения, если узел не является корнем дерева. Как только узел становится дочерним, его размер и ранг больше никогда не будут доступны.
Временная сложность
[ редактировать ]Реализация леса с непересекающимся множеством, в которой Find
не обновляет родительские указатели, и в котором Union
не пытается контролировать высоту деревьев, может иметь деревья высотой O ( n ) . В такой ситуации Find
и Union
операции требуют времени O ( n ) .
Если реализация использует только сжатие пути, то последовательность n MakeSet
операций, за которыми следует до n − 1 Union
операции и f Find
операций, имеет наихудшее время работы . [11]
Использование объединения по рангу, но без обновления родительских указателей во время Find
, дает время работы для m операций любого типа, до n из которых MakeSet
операции. [11]
Сочетание сжатия пути, разделения или деления пополам с объединением по размеру или рангу сокращает время выполнения m операций любого типа, до n из которых MakeSet
операции, чтобы . [4] [5] Это делает амортизированное время выполнения каждой операции . Это асимптотически оптимально, означающее, что каждая структура данных непересекающегося множества должна использовать амортизированное время на одну операцию. [6] Здесь функция – обратная функция Аккермана . Обратная функция Аккермана растет необычайно медленно, поэтому этот коэффициент равен 4 или меньше для любого n , которое действительно можно записать в физической вселенной. Это делает операции с непересекающимися множествами практически амортизированными за постоянное время.
Доказательство временной сложности Union-Find O(m log* n)
[ редактировать ]Точный анализ производительности непересекающегося леса довольно сложен. Однако существует гораздо более простой анализ, который доказывает, что амортизированное время для любого m Find
или Union
операций над лесом из несвязного множества, содержащим n объектов, равна O ( m log * n ) , где log * обозначает повторный логарифм . [12] [13] [14] [15]
Лемма 1. Поскольку функция поиска следует по пути к корню, ранг узла, с которым она сталкивается, увеличивается.
утверждают, что, поскольку к набору данных применяются операции поиска и объединения, этот факт остается верным с течением времени. Первоначально, когда каждый узел является корнем собственного дерева, это тривиально верно. Единственный случай, когда ранг узла может быть изменен, — это объединения по рангу применение операции . В этом случае дерево с меньшим рангом будет привязано к дереву с большим рангом, а не наоборот. А во время операции поиска все узлы, посещенные по пути, будут присоединены к корню, который имеет больший ранг, чем его дочерние элементы, поэтому данная операция также не изменит этого факта.
Лемма 2: Узел u , являющийся корнем поддерева ранга r, имеет как минимум узлы.
Первоначально, когда каждый узел является корнем собственного дерева, это тривиально верно. Предположим, что узел u ранга r имеет не менее 2 р узлы. Тогда, когда два дерева ранга r объединяются с помощью операции Union by Rank , получается дерево ранга r + 1 , корень которого имеет не менее узлы.
Лемма 3. Максимальное количество узлов ранга r не более
Из леммы 2 мы знаем, что узел u , являющийся корнем поддерева ранга r, имеет не менее узлы. мы получим Максимальное количество узлов ранга r , когда каждый узел ранга r является корнем дерева, имеющего ровно узлы. В этом случае количество узлов ранга r равно
Для удобства мы определяем здесь «ведро»: ведро — это набор, содержащий вершины с определенными рангами.
Мы создаем несколько сегментов и индуктивно помещаем в них вершины в соответствии с их рангами. То есть вершины с рангом 0 попадают в нулевую корзину, вершины с рангом 1 — в первую корзину, вершины с рангами 2 и 3 — во вторую корзину. Если B -е ведро содержит вершины с рангами из интервала тогда (B+1)-е ведро будет содержать вершины с рангами из интервала
Мы можем сделать два замечания относительно ведер.
- Общее количество сегментов не превышает log * н
- Доказательство: Когда мы переходим от одного ведра к другому, мы прибавляем к степени еще одну двойку, то есть следующее ведро к будет
- Максимальное количество элементов в сегменте самое большее
- Доказательство: максимальное количество элементов в ведре. самое большее
Пусть F представляет собой список выполненных операций «поиска», и пусть
Тогда общая стоимость m находок равна
Поскольку каждая операция поиска совершает ровно один обход, ведущий к корню, имеем T 1 = O ( m ) .
Кроме того, исходя из приведенной выше оценки количества сегментов, мы имеем T 2 = O ( m log * н ) .
для T3 Предположим, что мы пересекаем ребро от u до v , где u и v имеют ранги в ведре [ B , 2 Б − 1] и v не является корнем (во время этого обхода, иначе обход был бы учтен в T 1 ). Зафиксируем u и рассмотрим последовательность которые играют роль v в различных операциях поиска. Из-за сжатия пути и отсутствия учета ребра до корня эта последовательность содержит только разные узлы, и благодаря лемме 1 мы знаем, что ранги узлов в этой последовательности строго возрастают. Поскольку оба узла находятся в ведре, мы можем заключить, что длина k последовательности (количество раз, когда узел u присоединен к другому корню в одном и том же ведре) не превышает количества рангов в ведре B , что это, самое большее
Поэтому,
Из наблюдений 1 и 2 можно сделать вывод, что
Поэтому,
Другие структуры
[ редактировать ]Лучшее время на операцию в худшем случае
[ редактировать ]Самое худшее время Find
операция в деревьях с объединением по рангу или объединением по весу (т.е. это и эта граница точная).
В 1985 году Н. Блюм дал реализацию операций, не использующую сжатия путей, но сжимающую деревья во время . Его реализация выполняется в время на операцию, [16] и, таким образом, по сравнению со структурой Галлера и Фишера она имеет лучшее время на операцию в худшем случае, но худшее амортизированное время. В 1999 году Альструп и др. дал структуру, которая имеет оптимальный наихудший случай
время вместе с амортизированным временем обратного Аккермана. [17]
Удаление
[ редактировать ]Обычная реализация в виде непересекающихся лесов плохо реагирует на удаление элементов.
в том смысле, что пришло время Find
не улучшится в результате уменьшения количества элементов. Однако существуют современные реализации, которые допускают удаление в постоянное время и где ограничение по времени для Find
зависит от текущего количества элементов [18] [19]
Приложения
[ редактировать ]Структуры данных с непересекающимися наборами моделируют разделение набора , например, для отслеживания связанных компонентов неориентированного графа . Затем эту модель можно использовать, чтобы определить, принадлежат ли две вершины одному и тому же компоненту или приведет ли добавление ребра между ними к циклу. Алгоритм Union-Find используется в высокопроизводительных реализациях унификации . [20]
Эта структура данных используется библиотекой Boost Graph для реализации функций дополнительных связанных компонентов . Это также ключевой компонент в реализации алгоритма Краскала для поиска минимального остовного дерева графа.
Алгоритм Хошена-Копельмана использует в алгоритме поиск объединения.
См. также
[ редактировать ]- Уточнение разделов — другая структура данных для поддержки непересекающихся наборов с обновлениями, которые разделяют наборы на отдельные, а не объединяют их вместе.
- Динамическое подключение
Ссылки
[ редактировать ]- ↑ Перейти обратно: Перейти обратно: а б с д и ж Тарьян, Роберт Эндре (1975). «Эффективность хорошего, но не линейного алгоритма объединения множеств». Журнал АКМ . 22 (2): 215–225. дои : 10.1145/321879.321884 . hdl : 1813/5942 . S2CID 11105749 .
- ^ Галлер, Бернард А .; Фишер, Майкл Дж. (май 1964 г.). «Улучшенный алгоритм эквивалентности» . Коммуникации АКМ . 7 (5): 301–303. дои : 10.1145/364099.364331 . S2CID 9034016 . . Статья о возникновении непересекающихся лесов.
- ^ Хопкрофт, JE ; Ульман, доктор медицинских наук (1973). «Настройка алгоритмов слияния». SIAM Journal по вычислительной технике . 2 (4): 294–303. дои : 10.1137/0202024 .
- ↑ Перейти обратно: Перейти обратно: а б с Тарьян, Роберт Э .; ван Леувен, Ян (1984). «Анализ наихудшего случая алгоритмов объединения множеств» . Журнал АКМ . 31 (2): 245–281. дои : 10.1145/62.2160 . S2CID 5363073 .
- ↑ Перейти обратно: Перейти обратно: а б Тарьян, Роберт Эндре (1979). «Класс алгоритмов, которым требуется нелинейное время для поддержания непересекающихся множеств» . Журнал компьютерных и системных наук . 18 (2): 110–127. дои : 10.1016/0022-0000(79)90042-4 .
- ↑ Перейти обратно: Перейти обратно: а б Фредман, М .; Сакс, М. (май 1989 г.). «Сложность клеточного зондирования динамических структур данных». Материалы двадцать первого ежегодного симпозиума ACM по теории вычислений - STOC '89 . стр. 345–354. дои : 10.1145/73007.73040 . ISBN 0897913078 . S2CID 13470414 .
Теорема 5. Любая реализация CPROBE(log n ) задачи объединения множеств требует Ω( m α( m , n )) времени для выполнения m операций поиска и n −1 операций объединения, начиная с n одноэлементных множеств.
- ^ Галил, З.; Итальяно, Г. (1991). «Структуры данных и алгоритмы для задач объединения непересекающихся множеств». Обзоры вычислительной техники ACM . 23 (3): 319–344. дои : 10.1145/116873.116878 . S2CID 207160759 .
- ^ Андерсон, Ричард Дж.; Уолл, Хизер (1994). Параллельные алгоритмы без ожидания для задачи поиска объединений . 23-й симпозиум ACM по теории вычислений. стр. 370–380.
- ^ Коншон, Сильвен; Филлиатр, Жан-Кристоф (октябрь 2007 г.). «Постоянная структура данных Union-Find». Семинар ACM SIGPLAN по машинному обучению . Фрайбург, Германия.
- ^ Гарольд Н. Габоу, Роберт Эндре Тарьян, «Алгоритм линейного времени для особого случая объединения непересекающихся множеств», Журнал компьютерных и системных наук, том 30, выпуск 2, 1985, стр. 209–221, ISSN 0022- 0000, https://doi.org/10.1016/0022-0000(85)90014-5
- ↑ Перейти обратно: Перейти обратно: а б с Кормен, Томас Х .; Лейзерсон, Чарльз Э .; Ривест, Рональд Л .; Штейн, Клиффорд (2009). «Глава 21: Структуры данных для непересекающихся множеств». Введение в алгоритмы (Третье изд.). МТИ Пресс. стр. 571–572. ISBN 978-0-262-03384-8 .
- ^ Раймунд Зайдель , Миша Шарир. «Анализ сжатия пути сверху вниз», SIAM J. Comput. 34(3):515–525, 2005 г.
- ^ Тарьян, Роберт Эндре (1975). «Эффективность хорошего, но не линейного алгоритма объединения множеств» . Журнал АКМ . 22 (2): 215–225. дои : 10.1145/321879.321884 . hdl : 1813/5942 . S2CID 11105749 .
- ^ Хопкрофт, Дж. Э.; Ульман, доктор медицинских наук (1973). «Настройка алгоритмов слияния». SIAM Journal по вычислительной технике . 2 (4): 294–303. дои : 10.1137/0202024 .
- ^ Роберт Э. Тарьян и Ян ван Леувен . Анализ наихудшего случая алгоритмов объединения множеств. Журнал ACM, 31 (2): 245–281, 1984.
- ^ Блюм, Норберт (1985). «О временной сложности для одной операции в наихудшем случае задачи объединения непересекающихся множеств». 2-й симп. О теоретических аспектах информатики : 32–38.
- ^ Альструп, Стивен; Бен-Амрам, Амир М.; Рауэ, Тайс (1999). «Наихудший случай и амортизированная оптимальность при поиске объединения (расширенное резюме)». Материалы тридцать первого ежегодного симпозиума ACM по теории вычислений . стр. 499–506. дои : 10.1145/301250.301383 . ISBN 1581130678 . S2CID 100111 .
- ^ Альструп, Стивен; Торуп, Миккель; Гёрц, Инге Ли; Рауэ, Тайс; Цвик, Ури (2014). «Найти объединение с удалениями в постоянное время». Транзакции ACM на алгоритмах . 11 (1): 6:1–6:28. дои : 10.1145/2636922 . S2CID 12767012 .
- ^ Бен-Амрам, Амир М.; Йоффе, Саймон (2011). «Простой и эффективный алгоритм объединения-найти-удалить». Теоретическая информатика . 412 (4–5): 487–492. дои : 10.1016/j.tcs.2010.11.005 .
- ^ Найт, Кевин (1989). «Унификация: междисциплинарное исследование» (PDF) . Обзоры вычислительной техники ACM . 21 : 93–124. дои : 10.1145/62029.62030 . S2CID 14619034 .