Union-Find: unión rápida
Para un conjunto de datos, Union-Find soporta principalmente dos operaciones:
-
union(p,q) - Conectar los elementos p y q.
-
find(p)- Consultar en qué conjunto se encuentra el elemento p.
-
isConnected(p,q)- Verificar si los elementos p y q están conectados entre sí.
En la sección anterior, usamosiduna forma de arreglo para representar Union-Find; en la operación real, la complejidad temporal de la búsqueda esO(1), pero la eficiencia de la unión no es alta.
En esta sección, implementaremos Union-Find de otra manera. Cada elemento se considera un nodo que apunta a su nodo padre, y el nodo raíz se apunta a sí mismo. Como se muestra en la figura, el nodo 3 apunta al nodo 2, lo que significa que 3 y 2 están conectados; el nodo 2 es el nodo raíz, por lo que se apunta a sí mismo.

También usamos un arreglo para representar Union-Find, pero el siguiente grupo de elementos utilizaparentpara representar el nodo padre al que apunta el elemento actual. Cada elemento se apunta a sí mismo, todos son independientes.


Si en este momento se realiza la operaciónunion(4,3), apuntar el elemento 4 al elemento 3:

El arreglo también cambia en consecuencia:

Para determinar si dos elementos están conectados, solo hay que verificar si sus nodos raíz son iguales.
Como se muestra en la figura, los nodos raíz de los nodos 4 y 9 son ambos 8, por lo que están conectados.

Para conectar dos elementos, solo hay que encontrar sus nodos raíz correspondientes y hacer que los nodos raíz se conecten; así serán nodos conectados.
Supongamos que queremos conectar 6 y 4 en la figura anterior; solo necesitamos hacer que el nodo raíz 5 de 6 apunte al nodo raíz 8 de 4.

Para construir esta estructura de árbol que apunta al padre, se usa un arreglo para construir un árbol que apunta al padre; parent[i] representa el nodo padre al que apunta el elemento i.
private int[] parent;
private int count; // Número de elementos
...
Proceso de búsqueda: para encontrar el número de conjunto correspondiente al elemento p, se consulta repetidamente su nodo padre hasta llegar al nodo raíz. La característica del nodo raíz es parent[p] == p. Complejidad O(h), donde h es la altura del árbol.
private int find(int p){
assert( p >= 0 && p < count );
while( p != parent[p] )
p = parent[p];
return p;
}
...
Para fusionar los conjuntos a los que pertenecen los elementos p y q, se consultan los nodos raíz de ambos elementos y se hace que uno de los nodos raíz apunte al otro; así los dos conjuntos se fusionan. Esta operación tiene una complejidad temporal de O(h), donde h es la altura del árbol.
int pRoot = find(p);
int qRoot = find(q);
if( pRoot == qRoot )
return;
parent[pRoot] = qRoot;
}
Código de ejemplo en Java
Descargar paquete de código fuente:Download
Código del archivo UnionFind2.java:
/**
* Segunda versión de UnionFind
*/
public class UnionFind2 {
// Nuestra segunda versión de Union-Find: usa un arreglo para construir un árbol que apunta al padre
// parent[i] representa el nodo padre al que apunta el primer elemento
private int[] parent;
private int count; // Número de elementos
// Constructor
public UnionFind2(int count){
parent = new int[count];
this.count = count;
// Inicialización: cada parent[i] se apunta a sí mismo, lo que indica que cada elemento forma su propio conjunto
for( int i = 0 ; i < count ; i ++ )
parent[i] = i;
}
// Proceso de búsqueda: obtiene el número de conjunto correspondiente al elemento p
// Complejidad O(h), h es la altura del árbol
private int find(int p){
assert( p >= 0 && p < count );
// Consulta repetidamente su nodo padre hasta llegar al nodo raíz
// Característica del nodo raíz: parent[p] == p
while( p != parent[p] )
p = parent[p];
return p;
}
// Verifica si los elementos p y q pertenecen al mismo conjunto
// Complejidad O(h), h es la altura del árbol
public boolean isConnected( int p , int q ){
return find(p) == find(q);
}
// Fusiona los conjuntos de los elementos p y q
// Complejidad O(h), h es la altura del árbol
public void unionElements(int p, int q){
int pRoot = find(p);
int qRoot = find(q);
if( pRoot == qRoot )
return;
parent[pRoot] = qRoot;
}
}