Disjoint-Set Union Complexity

This section is reproduced and adapted from Time Complexity — A Brief Discussion of Potential Analysis, with the original author's permission.

Definitions

The Ackermann Function

We first define α(n)\alpha(n). To give this definition, we first define Ak(j)A_k(j).

Define Ak(j)A_k(j) as follows:

Ak(j)={j+1k=0Ak−1(j+1)(j)k≥1A_k(j)=\left\{ \begin{aligned} &j+1& &k=0&\\ &A_{k-1}^{(j+1)}(j)& &k\geq1& \end{aligned} \right.

That is, the Ackermann function.

Here, fi(x)f^i(x) denotes applying ff to xx consecutively ii times; that is, f0(x)=xf^0(x)=x and fi(x)=f(fi−1(x))f^i(x)=f(f^{i-1}(x)).

Now define α(n)\alpha(n) as the smallest integer such that Aα(n)(1)≥nA_{\alpha(n)}(1)\geq n. Note that we previously described it as Aα(n)(α(n))≥nA_{\alpha(n)}(\alpha(n))\geq n; in any case, they grow so slowly that their values do not exceed 4.

Basic Definitions

Every node has a rank. Here rank does not mean the number of nodes, but the depth. The initial rank of a node is 0. During a merge, if two nodes have different ranks, merge the lower-rank node into the higher-rank node without updating the higher-rank node's rank. Otherwise, randomly merge one node into the other and increase the root's rank by 1. The rank of the root gives the height of the tree. Let the rank of x be rnk(x)rnk(x), and similarly let the parent of x be fa(x)fa(x). We always have rnk(x)+1≤rnk(fa(x))rnk(x)+1\leq rnk(fa(x)).

To define the potential function, we first need to define an auxiliary function level(x)level(x). Let level(x)=max⁡(k:rnk(fa(x))≥Ak(rnk(x)))level(x)=\max(k:rnk(fa(x))\geq A_k(rnk(x))). When rnk(x)≥1rnk(x)\geq1, also define an auxiliary function iter(x)=max⁡(i:rnk(fa(x))≥Alevel(x)i(rnk(x))iter(x)=\max(i:rnk(fa(x))\geq A_{level(x)}^i(rnk(x)). The xx covered by these definitions always satisfies rnk(x)>0rnk(x)>0 and this xx is not the root of a tree.

The definitions above may be a little dizzying. Let us sort them out. For an xx and fa(x)fa(x), if rnk(x)>0rnk(x)>0, we can always find a pair i,ki,k such that rnk(fa(x))≥Aki(rnk(x))rnk(fa(x))\geq A_k^i(rnk(x)), while level(x)=max⁡(k)level(x)=\max(k) and, under this premise, iter(x)=max⁡(i)iter(x)=\max(i). levellevel describes the maximum iteration level of AA, while iteriter describes the maximum number of iterations at that maximum level.

For these two functions, level(x)level(x) always increases or remains unchanged as operations proceed. If level(x)level(x) does not increase, iter(x)iter(x) also only increases or remains unchanged. They always satisfy the following two inequalities:

0≤level(x)<α(n)0\leq level(x)<\alpha(n) 1≤iter(x)≤rnk(x)1\leq iter(x)\leq rnk(x)

Considering the definitions of level(x)level(x), iter(x)iter(x), and AkjA_k^j, these facts are easy to prove; we leave them to the reader as practice with the definitions.

Define the potential function Φ(S)=∑x∈SΦ(x)\Phi(S)=\sum\limits_{x\in S}\Phi(x), where SS denotes an entire disjoint-set union and xx is a node in the disjoint-set union. Define Φ(x)\Phi(x) as follows:

Φ(x)={α(n)×rnk(x)rnk(x)=0 或 x 为某棵树的根节点(α(n)−level(x))×rnk(x)−iter(x)otherwise\Phi(x)= \begin{cases} \alpha(n)\times \mathit{rnk}(x)& \mathit{rnk}(x)=0\ \text{或}\ x\ \text{为某棵树的根节点}\\ (\alpha(n)-\mathit{level}(x))\times \mathit{rnk}(x)-iter(x)& \text{otherwise} \end{cases}

We can now prove that the amortized time complexity is Θ(α(n))\Theta(\alpha(n)) through the change in potential caused by operations. Note that the union(x,y)union(x,y) operation considered here guarantees that xx and yy are both roots of trees, so it does not need to additionally execute find(x)find(x) and find(y)find(y).

It can be seen that the potential is always nonnegative. Also, at the beginning, the potential of the disjoint-set union is 00.

Proof

The union(x,y) Operation

Its cost is Θ(1)\Theta(1), so we consider the change in potential that it causes.

Here, assume rnk(x)≤rnk(y)rnk(x)\leq rnk(y), meaning that xx is attached to yy. Thus, the only nodes whose potential increases are xx (which changes from a tree root to a non-root), yy (whose rank may increase), and the children of yy before the operation (whose parent's rank may increase). We first prove that the potential of a child cc of yy before the operation cannot increase, and if it decreases, it decreases by at least 11.

Let the potential of cc before the operation be Φ(c)\Phi(c) and after the operation be Φ(c′)\Phi(c'). Here cc can be any non-root node with rnk(c)>0rnk(c)>0, and the operation can be any operation, including the find operation below. We discuss four cases.

  1. iter(c)iter(c) and level(c)level(c) do not increase. Clearly, Φ(c)=Φ(c′)\Phi(c)=\Phi(c').
  2. iter(c)iter(c) increases while level(c)level(c) does not. Here iter(c)iter(c) increases by at least one, so Φ(c′)≤Φ(c)−1\Phi(c')\leq \Phi(c)-1; the potential decreases by at least 1.
  3. level(c)level(c) increases, while iter(c)iter(c) may decrease. Since 0<iter(c)≤rnk(c)0<iter(c)\leq rnk(c), iter(c)iter(c) can decrease by at most rnk(c)−1rnk(c)-1, while level(c)level(c) increases by at least 11. By the definition Φ(c)=(α(n)−level(c))×rnk(c)−iter(c)\Phi(c)=(\alpha(n)-level(c))\times rnk(c)-iter(c), we obtain Φ(c′)≤Φ(c)−1\Phi(c')\leq\Phi(c)-1.
  4. Any other case. Since rnk(c)rnk(c) does not change and rnk(fa(c))rnk(fa(c)) does not decrease, this case cannot occur.

Therefore, the only nodes whose potential can increase are xx and yy. When xx changes from a tree root to a non-root, if rnk(x)=0rnk(x)=0, then Φ(x)=Φ(x′)=0\Phi(x)=\Phi(x')=0 always. Otherwise, we always have α(x)×rnk(x)≥(α(n)−level(x))×rnk(x)−iter(x)\alpha(x)\times rnk(x)\geq(\alpha(n)-level(x))\times rnk(x)-iter(x). Hence, Φ(x′)≤Φ(x)\Phi(x')\leq \Phi(x).

Thus, the only point whose potential may increase is yy. The potential of yy can increase by at most α(n)\alpha(n). Therefore, the amortized time complexity of the unionunion operation is Θ(α(n))\Theta(\alpha(n)).

The find(a) Operation

If the search path contains Θ(s)\Theta(s) nodes, its search time complexity is clearly Θ(s)\Theta(s). If the find operation increases the potential of no node and decreases the potential of at least s−α(n)s-\alpha(n) nodes by at least 11, then it can be proved that the amortized time complexity of find(a)find(a) is Θ(α(n))\Theta(\alpha(n)). To avoid confusion, we use aa as the parameter here, while every xx that appears refers generically to a node in a disjoint-set union.

First, prove that no node's potential increases. It is obvious: as proved above, the potential of every non-root node does not increase, and the rank rnkrnk of a root does not change, so no node's potential increases.

Next, prove that the potential of at least s−α(n)s-\alpha(n) nodes decreases by at least 11. We proved above that if level(x)level(x) or iter(x)iter(x) changes, their potential decreases by at least 11. Thus, it is enough to prove that level(x)level(x) or iter(x)iter(x) changes for at least s−α(n)s-\alpha(n) nodes.

Recall the definition of the potential of a non-root node, Φ(x)=(α(n)−level(x))×rnk(x)−iter(x)\Phi(x)=(\alpha(n)-level(x))\times rnk(x)-iter(x), where level(x)level(x) and iter(x)iter(x) are the maximum values such that rnk(fa(x))≥Alevel(x)iter(x)(rnk(x))rnk(fa(x))\geq A_{level(x)}^{iter(x)}(rnk(x)).

Therefore, if rootxroot_x represents the root of the tree containing xx, it is enough to prove that rnk(rootx)≥Alevel(x)iter(x)+1(rnk(x))rnk(root_x)\geq A_{level(x)}^{iter(x)+1}(rnk(x)). By the definition of AkiA_k^i, Alevel(x)iter(x)+1(rnk(x))=Alevel(x)(Alevel(x)iter(x)(rnk(x)))A_{level(x)}^{iter(x)+1}(rnk(x))=A_{level(x)}(A_{level(x)}^{iter(x)}(rnk(x))).

We may use k(x)k(x) to represent level(x)level(x) and i(x)i(x) to represent iter(x)iter(x) to avoid making the formulas too long. In this notation, rnk(rootx)≥Ak(x)(Ak(x)i(x)(x))rnk(root_x)\geq A_{k(x)}(A_{k(x)}^{i(x)}(x)).

When you reach this point, you may feel like “what on earth is this?”. This means that you may need to read it several more times, or skip some of the content and return later.

Here, we need an outer Ak(x)A_{k(x)}, which means that we may need to find another point yy. Let yy be the point after xx on the search path that satisfies k(y)=k(x)k(y)=k(x); here, “after the search path” is equivalent to “an ancestor of xx”. Clearly, not every xx has such a yy. It is easy to prove that there are at most α(n)+2\alpha(n)+2 values of xx without such a yy, because only the last xx for each kk, along with aa and rootaroot_a, has no such yy.

Let us emphasize once again that fa(x)fa(x) refers to the parent of xx before path compression; after path compression, the parent of xx is always denoted by rootxroot_x. For every xx for which yy exists, we have rnk(y)≥rnk(fa(x))rnk(y)\geq rnk(fa(x)). At the same time, we have rnk(fa(x))≥Ak(x)i(x)(rnk(x))rnk(fa(x))\geq A_{k(x)}^{i(x)}(rnk(x)). Since k(x)=k(y)k(x)=k(y), use kk as shorthand for both; that is, rnk(fa(x))≥Aki(x)(rnk(x))rnk(fa(x))\geq A_k^{i(x)}(rnk(x)). We need to construct one AkA_k, so we can ignore the value of iter(y)iter(y) and directly use the weakened form rnk(fa(y))≥Ak(rnk(y))rnk(fa(y))\geq A_k(rnk(y)).

If we combine the inequalities, something magical happens. We find that rnk(fa(y))≥Aki(x)+1(rnk(x))rnk(fa(y))\geq A_k^{i(x)+1}(rnk(x)). In other words, to iterate from rnk(x)rnk(x) to rnk(fa(y))rnk(fa(y)), we can iterate AkA_k at least i(x)+1i(x)+1 times without exceeding rnk(fa(y))rnk(fa(y)).

Clearly, rnk(rooty)≥rnk(fa(y))rnk(root_y)\geq rnk(fa(y)), and rnk(x)rnk(x) does not change during path compression. Therefore, we obtain rnk(rootx)≥Aki(x)+1(rnk(x))rnk(root_x)\geq A_k^{i(x)+1}(rnk(x)). In other words, the value of iter(x)iter(x) increases by at least 1; if rnk(x)rnk(x) does not increase, then level(x)level(x) must increase.

Therefore, Φ(x)\Phi(x) decreases by at least 1. Since there are at least s−α(n)−2s-\alpha(n)-2 such xx nodes, Φ(S)\Phi(S) ultimately decreases by at least s−α(n)−2s-\alpha(n)-2, and the amortized time complexity is therefore Θ(α(n)+2)=Θ(α(n))\Theta(\alpha(n)+2)=\Theta(\alpha(n)).

Why Disjoint-Set Union Can Be Broken

This question is asking what properties are destroyed if we do not use union by rank, causing the time complexity of disjoint-set union to no longer be guaranteed to be Θ(mα(n))\Theta(m\alpha(n)).

If, during merging, the node with larger rnkrnk is merged into the node with smaller rnkrnk, we set the rnkrnk-smaller node's rnkrnk value to the other node's rnkrnk value plus one. This guarantees rnk(fa(x))≥rnk(x)+1rnk(fa(x))\geq rnk(x)+1, so we avoid a situation where the properties fail in a way resembling a floor covered in compile errors.

Clearly, in this case, what we break is the statement in the union(x,y)union(x,y) function that “the potential of y can increase by at most α(n)\alpha(n)”.

There is a structure that can reduce the time complexity of path-compressed disjoint-set union to Ω(mlog⁡1+mnn)\Omega(m\log_{1+\frac{m}{n}}n), defined as follows:

A binomial tree (which is actually somewhat different from an ordinary binomial tree), where j is a constant and TkT_k consists of a Tk−1T_{k-1} with a Tk−jT_{k-j} added as a child of its root.

Binomial tree
Binomial tree

The boundary condition is that T1T_1 through TjT_j are each a single point.

Let rnk(Tk)=rkrnk(T_k)=r_k. Here we have rk=(k−1)/jr_k=(k-1)/j (proof omitted). In each round of operations, we attach it to a single-node tree and then query the bottom jj nodes. In other words, when we attach it to the single node, the potential of the single node increases by (k−1)/j+1(k-1)/j+1. When j=⌊mn⌋j=\lfloor\frac{m}{n}\rfloor, i=⌊log⁡j+1n2⌋i=\lfloor\log_{j+1}\frac{n}{2}\rfloor, and k=ijk=ij, the increase in potential is:

α(n)×((ij−1)/j+1)=α(n)×((⌊log⁡⌊mn⌋+1n2⌋×⌊mn⌋−1)/⌊mn⌋+1)\alpha(n)\times((ij-1)/j+1)=\alpha(n)\times((\lfloor\log_{\lfloor\frac{m}{n}\rfloor+1}\frac{n}{2}\rfloor\times \lfloor\frac{m}{n}\rfloor-1)/\lfloor\frac{m}{n}\rfloor+1)

After transforming this and removing all floor symbols, we obtain an increase in potential of ≥α(n)×(log⁡1+mnn−nm)\geq \alpha(n)\times(\log_{1+\frac{m}{n}}n-\frac{n}{m}). Over m operations, this is Ω(mlog⁡1+mnn−n)=Ω(mlog⁡1+mnn)\Omega(m\log_{1+\frac{m}{n}}n-n)=\Omega(m\log_{1+\frac{m}{n}}n).

About Union by Size

Since union by rank is harder to write than union by size, many experts choose to use union by size when implementing disjoint-set union. Specifically, maintain a size(x)size(x) for each root, and each time merge the smaller sizesize into the larger one.

So, can union by size be broken?

First, this can be explained from the properties of rank used in the proof. If sizesize can replace rnkrnk, then union by size can be used. To summarize, the properties involving rank in the proof are the following three:

  1. At most one node's rank increases on each merge, and it increases by at most 1.
  2. We always have rnk(fa(x))≥rnk(x)+1rnk(fa(x))\geq rnk(x)+1.
  3. A node's rank never decreases.

The second and third properties are clearly satisfied by sizsiz, but the first is not: if xx is merged into yy, then siz(y)siz(y) increases by as much as siz(x)siz(x).

Therefore, we can consider using log⁡2siz(x)\log_2 siz(x) in place of rnk(x)rnk(x).

For the first property, since a node's sizsiz can increase by at most a factor of two, log⁡2siz(x)\log_2 siz(x) increases by at most 1. The conclusion for the second and third properties is fairly obvious, so their proofs are omitted.

Thus, if you do not want to implement union by rank, just use union by size; the time complexity remains Θ(mα(n))\Theta(m\alpha(n)).

This content comes from OI Wiki: Disjoint-Set Union Complexity. See the original for authors and revision history. AIOJ has adapted the formatting, in-site links, and resource paths.

Content is shared under CC BY-SA 4.0 and SATA license; additional terms are governed by the original notice. Thanks to the OI Wiki authors and contributors; per the SATA terms, please visit the OI Wiki repository and star it to show your support.