Disjoint Set Union
Introduction
A Disjoint Set Union (DSU) is a data structure used to manage elements partitioned into disjoint sets. It is implemented as a forest, where each tree represents a set, and the nodes in the tree represent the elements in that set.
As the name suggests, DSU supports two operations:
- Union (Unite): Merge the sets containing two elements (merge the corresponding trees).
- Find (Query): Query which set an element belongs to (find the root node of the corresponding tree). This can be used to determine whether two elements belong to the same set.
With modifications, DSU can support deletion of single elements, moving elements, or maintaining edge weights on the tree. Using dynamic segment trees, it can also implement persistent DSU.
Warning
DSU cannot efficiently support the separation of sets.
Initialization
Initially, each element is in its own separate set, represented as a tree with only a root node. For convenience, we set the parent of the root node to itself.
Implementation
1 2 3 4 5 | |
1 2 3 | |
Find Operation
We need to move up the tree until we find the root node.
Implementation
1 | |
1 2 | |
Path Compression
Every element visited during the find operation belongs to the same set. We can directly attach them to the root node to speed up future queries.
Implementation
1 | |
1 2 3 4 | |
Union Operation
To merge two trees, we simply attach the root of one tree to the root of the other.
Implementation
1 | |
1 2 | |
Heuristic Merge (Union by Size/Rank)
When merging, choosing which root becomes the new root affects the complexity of future operations. We can attach the tree with fewer nodes or shallower depth to the other to avoid degeneration.
Detailed Complexity Discussion
Since we only need to support union and find operations, when merging two sets, attaching either set under the other yields a correct result. However, different attachment strategies lead to different time complexities. Specifically, if we attach a tree with fewer nodes and shallower depth under a larger tree, subsequent find operations will obviously be faster (and yield a better worst-case time complexity) compared to the alternative.
Of course, we don't always encounter sets that are strictly smaller in both size and depth. Since both size and depth are easy to maintain, we often choose one as the heuristic. Regardless of the choice, the time complexity is \(O(m\alpha(m,n))\). For a detailed proof, refer to the papers cited in the References.
In practice for competitive programming, even without heuristic merging, code often completes within time limits. In Tarjan's paper1, it is proven that using only path compression without heuristic merging has a worst-case time complexity of \(O(m \log n)\). In Yao's paper2, it is shown that using only path compression without heuristic merging has an average-case time complexity of \(O(m\alpha(m,n))\).
If only heuristic merging is used without path compression, the time complexity is \(O(m\log n)\). Since path compression in a single union may cause many modifications, it is sometimes unsuitable. For example, in persistent DSU or segment tree divide-and-conquer with DSU, DSU with only heuristic merging is generally used.
Reference implementation for union by size: (Note: the initialization method needs adjustment)
Implementation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
Reference Implementation
A complete implementation of DSU with path compression and union by size is as follows:
Template Problem Luogu P3367【Template】Disjoint Set Union Reference Implementation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | |
Complexity
With both path compression and heuristic merging, the average time per operation in DSU is only \(O(\alpha(n))\). Here, \(\alpha\) is the inverse of the Ackermann function, which grows extremely slowly. In other words, the average running time of a single DSU operation can be considered a very small constant. The proof of time complexity is on this page.
Inverse Ackermann Function
The Ackermann function \(A(m, n)\) is defined as:
\(A(m, n) = \begin{cases}n+1&\text{if }m=0\\A(m-1,1)&\text{if }m>0\text{ and }n=0\\A(m-1,A(m,n-1))&\text{otherwise}\end{cases}\)
The inverse Ackermann function \(\alpha(n)\) is defined as the inverse of the Ackermann function, i.e., the largest integer \(m\) such that \(A(m, m) \leqslant n\).
The space complexity of DSU is obviously \(O(n)\).
Extended Operations
Based on the ordinary DSU, a series of modifications can be made to support more operations or maintain more complex information.
DSU with Deletion
Ordinary DSU cannot support deletion because deleting a node inevitably deletes all nodes in the subtree rooted at it. To solve this, in DSU with deletion, we can ensure that all nodes storing actual data are always leaf nodes by creating virtual nodes. To do this, during initialization, a virtual node is created for each data node, and the parent of the data node is set to this virtual node. Since merging two sets always connects the two roots, only virtual nodes will ever have children. This ensures that deleting a node does not accidentally delete other nodes.
Note: after deleting a single node, a new virtual node must be created as its parent; otherwise, subsequent union and deletion operations cannot be performed correctly.
Template Problem SPOJ JMFILTER - Junk-Mail Filter Reference Implementation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | |
A similar method can also be used to implement moving a single element between sets. Implementation details are explained in the example problem.
Weighted DSU
We can also define some weight on the edges of the DSU and define how this weight is combined during path compression, thereby solving more problems. For example, for the classic "NOI2001 Food Chain", we can maintain an addition group modulo \(3\) on the edge weights. For such problems maintaining edge weights modulo a small modulus, we can also solve them by splitting a single node in the DSU into multiple states. This technique in special cases is also called "kind DSU" or "extended domain DSU". Examples later will illustrate these approaches.
To maintain edge weights in DSU, the edge weight needs to be stored in the child node. Therefore, each node stores the weight of the edge between itself and its parent. Only when a node's parent changes does the edge weight need to be adjusted accordingly. In general, this may happen during path compression and when merging two nodes. For example, if the edge weight is the distance from the current node to its parent, then during path compression, each time the current node's parent is replaced by the root, the distance from the parent to the root must be added to the edge weight stored in the current node; similarly, when merging two sets, the weight of the newly connected edge between the two roots needs to be computed.
Template Problem Library Checker - Unionfind with Potential Reference Implementation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | |
Example Problems
In algorithm competitions, problems directly testing DSU often require designing special structures for the problem.
UVa11987 Almost Union-Find
Implement a data structure similar to DSU, supporting the following operations:
- Merge the sets containing two elements.
- Move a single element to the set containing another element.
- Query the size and sum of elements in the set containing a given element.
Solution
In this problem, operations 1 and 3 are easy to handle; the difficulty lies in operation 2. Suppose we want to move element \(x\) to the set containing element \(y\). In ordinary DSU, directly setting the parent of element \(x\) to the root of the set containing \(y\) does not work because it would move the entire subtree rooted at \(x\). To solve this, we need to ensure element \(x\) has no children. For this purpose, when initializing the DSU, create a virtual node \(\tilde x\) for each element \(x\), and set the parent of element \(x\) to the corresponding virtual node \(\tilde x\). This way, when merging two sets, since we always connect one root to another, and all roots are virtual nodes, only virtual nodes will have children, while all nodes storing actual elements have no children. Now, moving an element becomes much easier to implement.
Reference Implementation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | |
Luogu P2024「NOI2011」Food Chain
There are three types of animals \(A,B,C\) in the animal kingdom, and their food chain forms an interesting cycle. \(A\) eats \(B\), \(B\) eats \(C\), \(C\) eats \(A\).
There are \(N\) animals, numbered \(1 \sim N\). Each animal is one of \(A,B,C\), but we do not know which one.
Someone describes the food chain relationships among these \(N\) animals in two ways:
- The first type of statement is
1 X Y, meaning \(X\) and \(Y\) are of the same type. - The second type of statement is
2 X Y, meaning \(X\) eats \(Y\).
This person says \(K\) statements one after another about the \(N\) animals using the above two forms. Some statements are true, some are false. A statement is false if it satisfies one of the following three conditions; otherwise, it is true.
- The statement contradicts some previous true statements;
- \(X\) or \(Y\) in the statement is greater than \(N\);
- The statement says \(X\) eats \(X\).
Your task is to output the total number of false statements given \(N\) and the \(K\) statements.
Solution 1
Consider using weighted DSU to maintain food chain information. If \(x\) and \(y\) are of the same type, then \(x\equiv y\pmod 3\); if \(x\) eats \(y\), then \(x - y \equiv 1 \pmod 3\). This transforms the problem into the template problem mentioned earlier.
Specifically, for each statement, excluding those obviously false due to \(x>n\) or \(y>n\), we need to check if \(x\) and \(y\) are already connected: if connected, compute the distance modulo 3 between them and compare with the information claimed by the statement; otherwise, connect them according to the information provided. Apart from obvious cases, a statement is false if and only if the two mentioned nodes are already connected and the corresponding distance contradicts the claimed information.
Reference Implementation 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | |
Solution 2
Split each creature \(x\) into three states. In implementation, we can directly treat different states as different elements:
- States in the same set as \(x\) are of the same species as \(x\);
- States in the same set as \(x+n\) can be eaten by \(x\);
- States in the same set as \(x+2n\) can eat \(x\).
Then, for a statement:
-
1 x yis false if and only if:- \(x>N\) or \(y>N\);
- \(y\) is in the same set as either \(x+n\) or \(x+2n\).
-
2 x yis false if and only if:- \(x>N\) or \(y>N\);
- \(y\) is in the same set as either \(x\) or \(x+2n\).
-
If true, merge the corresponding states.
Reference Implementation 2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | |
ABC396E Min of Restricted Sum
Given integers \(N, M\) and integer sequences of length \(M\): \(X=(X_1,X_2,\ldots,X_M)\), \(Y=(Y_1,Y_2,\ldots,Y_M)\), \(Z=(Z_1,Z_2,\ldots,Z_M)\). It is guaranteed that all elements of \(X\) and \(Y\) are between \(1\) and \(N\).
Define a non-negative integer sequence \(A=(A_1,A_2,\ldots,A_N)\) of length \(N\) as a good integer sequence if and only if it satisfies the following condition:
- For all integers \(i\) satisfying \(1 \leq i \leq M\), \(A_{X_i} \oplus A_{Y_i} = Z_i\), where \(\oplus\) denotes the XOR operation.
Determine whether such a good integer sequence exists. If it exists, find the good integer sequence that minimizes the sum of elements \(\displaystyle \sum_{i=1}^N A_i\), and output that sequence.
Solution
XOR is essentially a "same" or "different" relationship on individual binary bits. Therefore, by splitting all bits of \(A_i\), the XOR relationship can be maintained using weighted DSU (or kind DSU). Elements in the same connected component correspond to the same bit position of different numbers in \(A\). When computing the answer, elements in the same connected component are typically divided into two groups with opposite values. To minimize the total sum, assign the larger group to \(0\) and the smaller group to \(1\).
Reference Implementation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | |
Exercises
- 「NOI2015」Program Automatic Analysis
- 「JSOI2008」Star Wars
- 「NOIP2023」Three-Valued Logic
- 「NOI2002」Legend of Galactic Heroes
Other Applications
The Kruskal algorithm in Minimum Spanning Tree and the Tarjan algorithm in Lowest Common Ancestor are algorithms based on DSU.
Related topics can be found in DSU Applications.
References and Further Reading
- Zhihu Answer: Is there really a binary path compression optimization in DSU?
- Gabow, H. N., & Tarjan, R. E. (1985). A Linear-Time Algorithm for a Special Case of Disjoint Set Union. JOURNAL OF COMPUTER AND SYSTEM SCIENCES, 30, 209-221.PDF
- CSDN: Extended Domain DSU & Weighted DSU
-
Tarjan, R. E., & Van Leeuwen, J. (1984). Worst-case analysis of set union algorithms. Journal of the ACM (JACM), 31(2), 245-281.ResearchGate PDF ↩
-
Yao, A. C. (1985). On the expected performance of path compression algorithms.SIAM Journal on Computing, 14(1), 129-133. ↩
本页面最近更新:,更新历史
发现错误?想一起完善? 在 GitHub 上编辑此页!
本页面贡献者:OI-wiki
本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用