Árbol de Expansión Mínima
Árbol de Expansión Mínima
Árbol de Expansión Mínima
Árbol de Expansión
Dado un grafo conexo, no dirigido G. Un árbol de expansión es un árbol compuesto por todos los
vértices y algunas (posiblemente todas) de las aristas de G. Al ser creado un árbol no existirán ciclos,
además debe existir una ruta entre cada par de vértices.
Un grafo puede tener muchos arboles de expansión, veamos un ejemplo con el siguiente grafo:
En la imagen anterior se puede observar que el grafo dado posee 3 arboles de expansión, dichos arboles
cumplen con las propiedades antes mencionadas como son unir todos los vértices usando algunas aristas.
El problema de hallar el Árbol de Expansión Mínima (MST) puede ser resuelto con varios algoritmos,
los mas conocidos con Prim y Kruskal ambos usan técnicas voraces (greedy).
Algoritmo de Kruskal
Para poder comprender el algoritmo de kruskal será necesario revisar primer el tutorial de Union-Find.
Como trabaja:
Primeramente ordenaremos las aristas del grafo por su peso de menor a mayor. Mediante la técnica
greedy Kruskal intentara unir cada arista siempre y cuando no se forme un ciclo, ello se realizará
Como podemos ver en la imagen anterior la definición de nuestro grafo en código sería:
1
struct Edge{
2 int origen; //Vértice origen
3 int destino; //Vértice destino
4 int peso; //Peso entre el vértice origen y destino
5 Edge(){}
…
6 }arista[ MAX ]; //Arreglo de aristas para el uso en kruskal
7
Primeramente usaremos el método MakeSet de unión-find para inicializar cada componente, obteniendo
las siguientes componentes conexas iniciales:
Ahora el siguiente paso es ordenar las aristas del grafo en orden ascendente:
Para el ordenamiento podemos usar las librerías predefinidas de Java y C++ como estamos ordenando
estructuras necesitamos un comparador, en este caso estamos ordenando por peso por lo tanto dentro de
la estructura antes definida agregamos:
1
struct Edge{
2 …
3 //Comparador por peso, me servira al momento de ordenar lo realizara en orden as
4 //Cambiar signo a > para obtener el arbol de expansion maxima
5 bool operator<( const Edge &e ) const {
6 return peso < e.peso;
}
7 }arista[ MAX ]; //Arreglo de aristas para el uso en kruskal
8
Ordenamos el arreglo de aristas mediante lo siguiente:
La primera arista a verificar es la que une a los vértices 8 y 7, verificamos si están en la misma
componente, para ello hacemos Find(8) , Find(7):
Como podemos observar en la tabla y en la misma imagen no están en la misma componente conexa, por
tanto esta arista es valida para el MST así que unimos los vértices por el método de Union( 8 , 7 ).
Observamos la tabla de Union-Find y vemos que Find(3) != Find(9). Entonces es posible realizar la
unión de ambas componentes:
Continuamos con la siguiente arista:
En la imagen
podemos observar que ambos vértices no están en la misma componente, por tanto realizamos la Union(
6 , 7 ):
Continuamos con la siguiente arista, los vértices 1 y 2 no están en la misma componente conexa:
Realizamos la Union(1,2):
Al observar la imagen los vértices 3 y 6 están en distinta componentes conexas. Entonces realizamos la
Union(3,6) y actualizamos la tabla de Union-Find.
Continuamos con la siguiente arista:
En este caso si
observamos la imagen los vértices 7 y 9 están en la misma componente conexa; asimismo en la tabla de
Union-Find el elemento raíz del vértice 7 es el mismo que el del vértice 9 por ello afirmamos que están
el a misma componente conexa, por lo tanto no habrá que realizar la unión de ambos vértices. Con esto
evitamos tener ciclos en el árbol de expansión mínima.
Continuamos con la siguiente arista:
Al observar la
imagen los vértices 3 y 4 no están en la misma componente conexa por lo tanto realizamos la Union(3,4)
en el grafo:
Continuamos con la siguiente arista:
Los vértices 8 y
9 están en la misma componente conexa por lo tanto no realizamos Unión de vértices. Continuemos con
la siguiente arista:
Los vértices 1 y
8 están diferentes componentes. Realizamos la Union(1,8) en el grafo:
Continuamos con la siguiente arista:
Los vértices 2 y
3 están en la misma componente conexa por lo tanto no realizamos Union de componentes.
Continuamos con la siguiente arista:
El peso total del árbol de expansión mínima para el grafo mostrado es 39.
En código simplemente es iterar sobre el arreglo de aristas ingresado y ordenado obteniendo sus
respectivos datos. Para verificar si están o no en la misma componente usamos el método
sameComponent explicado en el tutorial de Union-Find:
1
2 for( int i = 0 ; i < E ; ++i ){ //Recorremos las aristas ya ordenadas por peso
origen = arista[ i ].origen; //Vértice origen de la arista actual
3
destino = arista[ i ].destino; //Vértice destino de la arista actual
4 peso = arista[ i ].peso; //Peso de la arista actual
5 //Verificamos si estan o no en la misma componente conexa
6 if( !sameComponent( origen , destino ) ){ //Evito ciclos
7 total += peso; //Incremento el peso total del MST
MST[ numAristas++ ] = arista[ i ]; //Agrego al MST la arista actual
8 Union( origen , destino ); //Union de ambas componentes en una sola
9 }
10 }
11
Verificación de MST
Para que sea un MST válido el número de aristas debe ser igual al número de vértices – 1. Esto se
cumple debido a que el MST debe poseer todos los vértices del grafo ingresado y además no deben
existir ciclos. Si vemos el ejemplo antes explicado tenemos en el MST:
Número de Aristas = 8
Número de Vértices = 9
Cumple con lo dicho -> 9 – 1 = 8 por tanto tenemos un MST válido. Veamos otro ejemplo teniendo
como grafo ingresado lo siguiente:
Como podemos observar el grafo ingresado posee 2 componentes conexas, al aplicar kruskal
obtendremos los siguientes MST:
En la imagen podemos observar el MST luego de aplicar kruskal, sin embargo no es un MST válido
porque no tiene 1 componente conexa que posea todos los vértices, comprobemos:
Número de Aristas = 7
Número de Vértices = 9
No cumple lo dicho inicialmente 9 – 1 != 7 por lo tanto tenemos un MST invalido. En código basta con
un if:
1 //Si el MST encontrado no posee todos los vértices mostramos mensaje de error
2 //Para saber si contiene o no todos los vértices basta con que el numero
//de aristas sea igual al numero de vertices - 1
3 if( V - 1 != numAristas ){
4 puts("No existe MST valido para el grafo ingresado, el grafo debe ser conexo.");
5 return;
6 }
7
Problemas de diferentes Jueces
UVA
908 – Re-connecting Computer Sites
1208 – Oreon
10034 – Freckles
10462 – Is There A Second Way Left?
10600 – ACM contest and Blackout
10842 – Traffic Flow
11228 – Transportation System
11631 – Dark roads
11710 – Expensive subway
11733 – Airports
11747 – Heavy Cycle Edges
11857 – Driving Range
COJ
1016 – Freckles
1690 – Bad Cowtractors
TOPCODER
SRM 356 DIV2-1000 – RoadReconstruction
SRM 492 DIV2 – 1000 – TimeTravellingSalesman
HDU
1102 – Constructing Roads
POJ
2377 – Bad Cowtractors
2421 – Constructing Roads
TJU
2531 – Oreon
Códigos:
Implementación del algoritmo en C++: Algoritmo de Kruskal
Implementación del algoritmo en JAVA: Algoritmo de Kruskal
Por Jhosimar George Arias Figueroa
SHARE THIS:
Twitter
Facebook17
Esta entrada fue publicada en Algorithms, Main y etiquetada graph, kruskal, minimum spanning
tree, mst, union find. Guarda el enlace permanente.