Conectividad

Union-Find (Conjuntos Disjuntos) Explicado, con Código

Union-find responde a una pregunta engañosamente simple: ¿están estas dos cosas conectadas? Lo hace en tiempo casi constante, y es el motor silencioso detrás del algoritmo de Kruskal, la detección de ciclos e incontables problemas de entrevista. Aquí verás cómo funciona y por qué es tan rápido.

11 Min de lectura Actualizado: Julio 2026 Apto para Principiantes
Mohammed Islam Hadjoudj
Mohammed Islam Hadjoudj
Expert Operations Research Engineer

¿Qué Es Union-Find?

Union-find, también llamado disjoint set union (DSU), es una estructura de datos que rastrea un conjunto de elementos divididos en grupos que no se solapan. Cada elemento pertenece exactamente a un grupo, y cada grupo se identifica por un único representante, su raíz.

El truco está en cómo se almacenan los grupos: como un bosque de árboles. Cada elemento tiene un puntero al padre, y seguir a los padres hacia arriba siempre lleva a la raíz del grupo. Dos elementos están en el mismo grupo si y solo si comparten la misma raíz. Esa es toda la idea, y todo lo demás consiste en hacerla rápida.

Las Dos Operaciones

Union-find admite exactamente dos operaciones, y toda su reputación descansa en hacer ambas casi al instante.

Como los grupos solo se fusionan y nunca se dividen, union-find es perfecto para problemas donde las conexiones se añaden con el tiempo pero nunca se eliminan.

La Versión Ingenua y Su Problema

Un primer intento solo guarda punteros al padre y fusiona apuntando una raíz a la otra. Funciona, pero tiene un fallo desagradable: nada impide que los árboles crezcan hasta ser cadenas largas. Si cada unión apila un nodo sobre el último, find tiene que recorrer una cadena de longitud n, y cada operación se degrada a O(n).

Eso no es mejor que una lista simple. La solución son dos pequeños cambios que, juntos, son uno de los resultados más célebres en estructuras de datos.

Dos Optimizaciones Que lo Cambian Todo

La unión por rango mantiene los árboles poco profundos. Al fusionar dos grupos, cuelga siempre el árbol más corto bajo la raíz del más alto. Un árbol corto colgado de uno alto no aumenta la altura, así que los árboles se mantienen planos.

La compresión de caminos aplana sobre la marcha. Cada vez que find sube hasta una raíz, vuelve a apuntar cada nodo por el que pasó directamente a esa raíz. El siguiente find sobre cualquiera de ellos es entonces un solo salto. La figura de abajo muestra un find colapsando una cadena.

Before: find(4) After: path compression 1 2 3 4 1 2 3 4
Un find(4) sube hasta la raíz 1 y luego vuelve a apuntar cada nodo por el que pasó directamente a 1. La cadena se convierte en un árbol plano.
Usadas juntas, la unión por rango y la compresión de caminos mantienen cada árbol casi completamente plano, así que ambas operaciones se ejecutan en tiempo casi constante. Cualquiera por sí sola ayuda; las dos juntas son lo que hace famoso a union-find.

Implementación en Python

Toda la estructura cabe en una clase pequeña. Dos arreglos hacen todo el trabajo: parent y rank.

class UnionFind:
    def __init__(self, n):
        self.parent = list(range(n))   # cada elemento empieza siendo su propia raíz
        self.rank = [0] * n            # una cota superior de la altura de cada árbol

    def find(self, x):
        # Compresión de caminos: apunta x directamente a la raíz.
        if self.parent[x] != x:
            self.parent[x] = self.find(self.parent[x])
        return self.parent[x]

    def union(self, a, b):
        ra, rb = self.find(a), self.find(b)
        if ra == rb:
            return False               # ya en el mismo grupo

        # Unión por rango: cuelga el árbol más corto bajo el más alto.
        if self.rank[ra] < self.rank[rb]:
            ra, rb = rb, ra
        self.parent[rb] = ra
        if self.rank[ra] == self.rank[rb]:
            self.rank[ra] += 1
        return True

Fíjate en que union devuelve False cuando los dos elementos ya estaban conectados. Ese único booleano es lo que hace tan limpias la detección de ciclos y el algoritmo de Kruskal: si una unión falla, la arista que ibas a añadir habría cerrado un ciclo.

Complejidad: Casi Constante

Con ambas optimizaciones, una secuencia de m operaciones sobre n elementos se ejecuta en O(m · α(n)) en total, donde α es la función inversa de Ackermann.

VersiónPor operaciónNota
IngenuaO(n)Los árboles pueden degradarse a cadenas
Solo unión por rangoO(log n)Los árboles se mantienen equilibrados
Solo compresión de caminosO(log n) amortizadoSe aplana con el tiempo
Ambas juntasO(α(n)) amortizadoEn la práctica constante

La función inversa de Ackermann crece tan despacio que α(n) es como mucho 4 para cualquier n que quepa en el universo observable. En la práctica, trata cada operación como tiempo constante. Para ver cómo encaja entre todos los algoritmos de grafos, consulta la guía de complejidad y la chuleta.

Dónde Se Usa

Union-find aparece siempre que necesitas rastrear la conectividad a medida que crece.

También está en la Etapa 3 de la hoja de ruta de teoría de grafos, justo donde aprendes los árboles de expansión.

Ve fusionarse los grupos en tiempo real

Union-find encaja cuando ves unirse dos árboles y colapsar un camino. Explóralo dentro del algoritmo de Kruskal en un grafo en vivo.

Abrir el Visualizador de Algoritmos

Preguntas Frecuentes

¿Para qué se usa union-find?

Union-find, también llamado disjoint set union (DSU), rastrea una colección de elementos divididos en grupos que no se solapan. Responde rápido a dos preguntas: si dos elementos están en el mismo grupo, y fusionar los grupos que contienen a dos elementos. Impulsa las consultas de conectividad, la detección de ciclos y el árbol de expansión mínima de Kruskal.

¿Cuál es la complejidad temporal de union-find?

Con compresión de caminos y unión por rango, cada find o union se ejecuta en tiempo amortizado O(alpha(n)), donde alpha es la función inversa de Ackermann. Para cualquier entrada que veas, alpha(n) es como mucho 4, así que cada operación es en la práctica de tiempo constante.

¿Cuál es la diferencia entre unión por rango y compresión de caminos?

La unión por rango mantiene los árboles poco profundos al colgar siempre el árbol más corto bajo el más alto durante una unión. La compresión de caminos aplana el árbol durante un find al apuntar cada nodo visitado directamente a la raíz. Usadas juntas, dan un tiempo casi constante por operación.

¿Dónde se usa union-find en grafos?

Los usos clásicos son el algoritmo de Kruskal para el árbol de expansión mínima, detectar ciclos en un grafo no dirigido, contar componentes conexas y cualquier problema de conectividad dinámica donde las aristas se añaden con el tiempo.

Recursos Adicionales de Aprendizaje

Míralo, No Solo lo Leas

Union-find es más fácil de creer cuando ves al algoritmo de Kruskal usarlo para construir un árbol de expansión, arista por arista. Carga un grafo y dale a reproducir.

Practica con el Visualizador de Algoritmos