Union-Find
El algoritmo de Kruskal para encontrar el árbol cobertor mínimo de un grafo crea una cola de prioridad con todas las aristas y parte con un bosque (visto como conjunto de aristas). Luego va tomando cada arista, y si no forma ciclo en el bosque, la agrega a , terminando cuando y el bosque se ha convertido en un árbol. En un grafo conexo de nodos y aristas, el algoritmo tiene un peor caso de para manejar la cola de prioridad. Sin embargo, en esta formulación no queda claro cómo determinar si una arista forma ciclo o no.
Si pensamos los árboles de como clases de equivalencia formadas por nodos, una arista forma ciclo sii y son nodos del mismo árbol, es decir, están en la misma clase de equivalencia. Cuando no lo están, agregar la arista tiene el efecto de unir los dos árboles, es decir, las dos clases de equivalencia. Podemos entonces reexpresar las operaciones que necesitamos como operaciones que manejan clases de equivalencia:
- Partimos con cada uno de los elementos formando su propia clase.
- Podemos preguntar si dos elementos pertenecen a la misma clase.
- Podemos unir dos clases.
La interfaz que veremos define un elemento de cada clase, en forma arbitraria pero consistente, como su representante. Tenemos entonces las dos operaciones siguientes:
- entrega el representante de la clase de equivalencia de .
- une las clases de equivalencia representadas por y por .
--> imagen
Dos elementos y pertenecen entonces a una misma clase sii , y para unir las clases de dos elementos cualquiera (no necesariamente representantes de clase) y realizamos . Con esta interfaz, el algoritmo de Kruskal realiza operaciones y operaciones . Veremos primero una solución de tiempo para estas operaciones y luego una mucho mejor, que requiere análisis amortizado. Con estas soluciones, el costo del algoritmo de Kruskal es , dominado por las operaciones en la cola de prioridad.
📄️ Solución de tiempo $O(\log n)$
Obviando las soluciones muy elementales, que requieren tiempo $O(n)$ para
📄️ Solución de tiempo amortizado $O(\log^* n)$
Cuando se realiza una operación $Find(v)$, se visitan todos los ancestros de