Edges are added to a graph and at any moment we ask: are a and b in the same component? Running BFS/DFS each time costs O(V + E). Union-Find (Disjoint Set Union, DSU) keeps the components as sets and answers this almost in O(1).

Path compression on the left, union by size on the right
Path compression on the left, union by size on the right

The idea: a forest

Every set is a tree and the root of the tree is the representative of the set. The forest lives in one array: parent[i] is the parent of i, and r is a root when parent[r] == r. find(x) walks up to the root and union(a, b) hangs one root under the other.

If we always hang the same side the tree degenerates into a chain and find becomes O(n). Below is the difference on 1000 consecutive unions.

python
def depth(parent, x):
    d = 0
    while parent[x] != x:
        x, d = parent[x], d + 1
    return d

def find(parent, x):
    while parent[x] != x:
        x = parent[x]
    return x

n = 1000
naive = list(range(n))
by_size, size = list(range(n)), [1] * n
for i in range(n - 1):
    naive[find(naive, i)] = find(naive, i + 1)          # always hang the left root
    a, b = find(by_size, i), find(by_size, i + 1)
    if size[a] < size[b]:
        a, b = b, a
    by_size[b] = a                                     # small tree under the large
    size[a] += size[b]