Capitulo 22
Capitulo 22
Capitulo 22
Contenido
Competencias especficas
Analizar y resolver problemas de la vida real que pueden ser representados con grafos.
606
22.1 Introduccin
La teora de Grafos fue desarrollada en 1736 por el matemtico ruso Leonhard Euler para
resolver el problema de los 7 puentes de la ciudad de Knigsberg.
El problema consista en determinar la existencia de algn camino que, partiendo desde
un punto de la ciudad, permitiera recorrer los 7 puentes pasando solo una vez por cada
uno y regresar al mismo punto de inicio.
Como vemos en la siguiente figura, la ciudad es atravesada por un ro y entre costa y
costa hay dos islas que, en aquella poca, estaban unidas entre s y, tambin, entre las
dos costas mediante 7 puentes.
Para solucionar el problema, Euler utiliz una simplificacin del mapa representando a las
regiones terrestres con puntos o crculos y a los puentes con lneas.
En esta abstraccin Euler pudo observar que los puntos por los que el recorrido fuese
a pasar deberan estar conectados por una cantidad par de lneas ya que, si llegamos a
ese punto a travs de un puente entonces, necesariamente, para retirarnos tendremos
que utilizar un puente diferente.
Dado que en este problema todos los puntos estn conectados con una cantidad impar
de lneas, la conclusin a la que lleg fue que no existe un camino con las caractersticas
del que buscamos. Luego, en este caso el problema no tiene solucin.
Sin embargo, la abstraccin desarrollada por Euler dio origen a la idea de grafo que, en
la actualizad, tiene aplicacin en diversos campos, como ser: fsica, qumica, arquitectura, ingeniera e informtica.
Con un grafo podemos representar una red de carreteras que unen diferentes ciudades y
analizar, por ejemplo, cul es el camino ms corto para llegar desde una ciudad hasta otra.
607
0
2
3
6
Fig. 22.3 Grafo.
Cada vrtice del grafo se identifica con una etiqueta numrica o alfanumrica que lo diferencia de todos los dems. Por ejemplo, en el grafo de la figura anterior, los vrtices estn
etiquetados con valores numricos consecutivos, comenzando desde cero.
0
2
3
6
Fig. 22.4 Grafo no dirigido.
6
Fig. 22.5 Grafo dirigido.
608
22.2.4 Ciclos
Llamamos ciclo a un camino que, sin pasar dos veces por la misma arista, comienza y
finaliza en el mismo vrtice. Por ejemplo, en el dgrafo de la figura anterior algunos de los
ciclos que podemos identificar son:
c1 = {0, 3, 6, 5, 4, 1, 0}
c2 = {0, 3, 5, 4, 1, 0}
c3 = {3, 6, 5, 2, 3}
c4 = {5, 4, 1, 2, 3, 5}
Y en el grafo no dirigido ubicado a la izquierda podemos destacar los siguientes ciclos:
c1 = {0, 1, 4, 2, 3, 0}
c2 = {2, 1, 0, 3, 2}
c3 = {6, 5, 4, 2, 1, 0, 3, 6}
22.2.5 rboles
Un rbol es un grafo conexo y sin ciclos.
En la siguiente figura, a la izquierda, vemos el grafo con el que hemos estado trabajando
desde el principio del captulo. Luego, a la derecha, observamos cmo quedara el grafo
si suprimimos, arbitrriamente, algunas de sus aristas para eliminar la existencia de ciclos. Por ejemplo, suprimimos las aristas: (0, 3), (1, 4), (4, 5), (2, 3), (3, 6).
0
2
3
6
609
Luego, simplemente reacomodamos los vrtices para poder visualizar el grafo con el
formato natural de los rboles.
2
1
5
4
0
6
4
6
6
4
6
4
5
2
4
6
Por ejemplo, si los nodos del grafo representan ciudades entonces los valores de las
aristas podran representar las distancias que existen entre estas. O tambin podran
representar el costo que implicara trasladarnos desde una ciudad hacia otra.
610
4
0
1
2
3
4
5
6
0
2
0
0
1
0
1
0
0
0
1
1
0
1
0
1
0
0
2
0
1
0
1
1
1
0
3
1
0
1
0
0
1
1
4
0
1
1
0
0
1
0
5
0
0
1
1
1
0
1
6
0
0
0
1
0
1
0
6
Fig. 22.10 Matriz de adyacencias de un grafo no dirigido ni ponderado.
4
6
6
4
6
4
5
2
4
6
0
1
2
3
4
5
6
0
0
6
0
6
0
0
0
1
6
0
2
0
4
0
0
2
0
2
0
4
6
4
0
3
6
0
4
0
0
2
6
4
0
4
6
0
0
6
0
5
0
0
4
2
6
0
4
6
0
0
0
6
0
4
0
6
4
4
6
611
4
2
0
1
2
3
4
5
6
3
6
2
4
4
6
4
6
4
package libro.cap22;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
public class Grafo
{
// matriz de adyacencias
612
// :
// viene de mas arriba
// retorna la longitud de la matriz de adyacencias
public int size()
{
return matriz.length;
}
// retorna el valor de matriz[a][b]
public int getDistancia(int a, int b)
{
return matriz[a][b];
}
// sigue mas abajo
// :
Ahora agregaremos dos constructores alternativos. El primero solo recibir un valor entero indicando la dimensin de la matriz de adyacencias. Luego instanciar e inicializar la
variable de instancia matriz asignando valores infinito en cada una de sus celdas. El
segundo constructor alternativo permitir construir un grafo con n nodos y especificar las
adyacencias mediante un conjunto de aristas, cada una de las cuales estar determinada
por dos vrtices y una distancia. Las aristas las representaremos con instancias de la clase
Arista cuyo cdigo veremos a continuacin.
package libro.cap22;
public class Arista
{
private int n1;
private int n2;
private int distancia;
// constructor
public Arista(int n1,int n2, int dist)
{
this.n1 = n1;
this.n2 = n2;
this.distancia = dist;
}
// determinar si "yo", que soy una arista, soy igual a otra
public boolean equals(Object a)
{
Arista otra = (Arista)a;
// :
// settters y getters
// :
613
// :
// viene de mas arriba
// recibe la dimension de la matriz, la instancia
// y le asigna "infinito" a cada celda
public Grafo(int n)
{
this(n);
for(Arista a:arr)
{
addArista(a);
}
Por ltimo, incluiremos un mtodo llamado getVecinos que retornar los vrtices adyacentes de un determinado nodo que recibir como parmetro.
614
// :
// viene de mas arriba
// retorna los nodos adyacentes
return a;
Enunciado
Dado un grafo G de n vrtices y un conjunto de aristas, se pide determinar si, al agregar
una arista adicional esta posibilitar o no la existencia de un ciclo. La cantidad de nodos,
las aristas iniciales y la arista que debemos analizar se ingresarn por consola.
Anlisis
Observemos el grafo de la siguiente figura:
0
2
3
6
Fcilmente, podemos observar que si agregamos una arista conectando los vrtices 3
y 6 se formar un ciclo. Lo mismo sucedera si agregsemos una arista que conecte los
vrtices 0 con 2, 3 con 4 o 4 con 6. Cualquier otra arista que agreguemos unir los dos
pequeos rboles, pero no formar ningn ciclo.
Agreguemos, por ejemplo, una conexin entre los vrtices 4 y 1:
615
vecinos(2) = {1}
Como 5 (el nodo final de la arista) no es vecino de 2, repetiremos la operacin procesando a cada uno de sus vecinos.
Nodo en proceso: 1
vecinos(1) = {0, 2, 4}
Como 5 tampoco est entre los vecinos de 1 repetiremos el proceso con cada uno de sus
vecinos comenzando, arbitrriamente, por el primero: 0.
Nodo en proceso: 0
vecinos(0) = {1}
Evidentemente, la estrategia nos condujo a un loop del cual no podremos salir ya que el
vecino de 0 es 1 y el primero de los vecinos de 1 es 0. La misma situacin se dar entre
los vrtices 2 y 1 ya que 1 es vecino de 2 y 2 es vecino de 1.
La solucin a este problema ser recordar los nodos que ya hemos procesado para no
volverlos a considerar. Siendo as, repetiremos el anlisis evaluando, nuevamente, a los
vecinos de 2, que es el vrtice inicial de la arista en cuestin.
Nodo en proceso: 2
vecinos(2) = {1}
Procesados = {2}
vecinos(1) = {0, 2, 4}
Procesados = {2,1}
Ignoramos a 2 porque recordamos que este nodo ya fue procesado. Como 5 no est
entre los vecinos de 1, nuevamente repetimos la operacin con cada uno de ellos.
Nodo en proceso: 0
vecinos(0) = {1}
Procesados = {0,2,1}
El nico vecino de 0 es 1 pero como ya lo hemos procesado lo descartamos y continuamos con el nodo 4 que, como vecino de 1, estaba pendiente de ser procesado.
Nodo en proceso: 4
vecinos(4) = {1,5}
Procesados = {0,2,1,4}
616
Podemos ver que partiendo desde 2, vrtice inicial de la arista que estamos analizando,
pudimos llegar hasta 5, el vrtice final. Esto nos permite determinar que, de agregarla al
grafo, estaremos formando un ciclo.
Para implementar el algoritmo utilizaremos un arraylist y una cola. El arraylist nos permitir recordar los nodos que ya hemos procesado. La cola nos permitir encolar los
vecinos de cada uno de los nodos a medida que los vayamos procesando.
Veamos el programa principal:
package libro.cap22.ciclos;
import libro.cap22.Arista;
import libro.cap22.Grafo;
import java.util.Scanner;
public class VerificaCiclos
{
public static void main(String[] args)
{
Scanner scanner = new Scanner(System.in);
// ingresa x consola los valores del grafo
Grafo g = ingresaGrafo(scanner);
// ingresa x consola los valores de la arista
Arista a = ingresaAristaAVerificar(scanner);
if( hayCiclo(g,a) )
{
System.out.println("La arista forma un ciclo");
}
else
{
System.out.println("La arista no forma ciclo");
}
// :
// viene de mas arriba
617
// extremos de la arista
q.add(nInicio);
while( !q.isEmpty() )
{
int n = q.poll();
if( n==nFin )
{
return true; // HAY CICLO
}
procesados.add(n);
for(int v:vecinos)
{
if( !procesados.contains(v) )
{
q.add(v);
}
}
return false;
Por ltimo, solo para completar el programa, veamos los mtodos que permiten el ingreso de datos por consola.
// :
// viene de mas arriba
618
return g;
return a;
619
// :
// :
Ahora s, modifiquemos el mtodo getVecinos para retornar solo aquellos nodos adyacentes que previamente no hayan sido marcados como procesados.
// :
// retorna los adyacentes que no han sido procesados
}
}
if(!procesados.contains(i))
{
a.add(i);
}
return a;
// :
620
de finalizar el algoritmo.
// :
for(int x: vecinos)
{
q.add(x);
}
procesados = bkpProcesados;
}
return false;
// :
621
4
6
6
4
6
4
5
2
4
6
Como vemos, de todos los caminos que conectan al nodo 0 con el nodo 5, el menos
costoso o el ms corto es: c8 = {0, 3, 5}.
package libro.cap22.dijkstra;
import java.util.Hashtable;
import libro.cap22.Grafo;
public interface Dijkstra
{
public Hashtable<Integer, Integer> procesar(Grafo g, int s);
}
622
Siendo as, queremos obtener las menores distancias existentes entre el nodo 4
y cada uno de los otros nodos del grafo.
4
6
5
2
4
6
Comenzaremos analizando sus vecinos, que son: {1, 2, 5}. Las distancias
que existen entre 4 y cada uno de estos nodos son, respectivamente: 4, 6 y
6. Por el momento consideraremos que
estas distancias son mnimas y, mientras no puedan ser mejoradas, formarn
parte del conjunto solucin. Luego procesaremos cada uno de los vecinos de
4 comenzando, arbitrriamente, por 5.
Los vecinos de 5, obviando a 4 que ya fue procesado, son: {2, 3, 6} y sus distancias son,
respectivamente, 4, 2 y 4. Sin embargo, para llegar a 5 desde 4 pagamos un costo inicial de 6. Por esto, momentneamente, las mnimas distancias que existen entre 4 y cada
uno de los vecinos de 5 son 6+4, 6+2 y 6+4.
Sean a y b dos nodos adyacentes de un grafo, llamaremos d(a,b) a la distancia directa
entre a y b, que coincide con el valor expresado en la arista que los une.
Veamos: pasando por 5, la distancia entre 4 y 3 es 8 ya que d(4,5)=6+d(5,3)=2. En algn
momento procesaremos el vrtice 2, vecino de 4, desde donde tambin podemos llegar
a 3. Sin embargo, para llegar a 3 desde el nodo de origen 4, pasando por 2, la distancia
total es 10 y se obtiene sumando: d(4,2)=6+d(2,3)=4. Este valor es peor que el costo de
llegar a 3 a travs de 5, razn por la cual lo descartaremos.
El algoritmo de Dijkstra consiste en procesar cada uno de los vecinos de s, el vrtice de
origen, luego los vecinos de los vecinos de s y as sucesivamente hasta llegar a procesar
todos los nodos del grafo.
Como los nodos deben procesarse solo una vez, utilizaremos la funcionalidad provista
por la clase Grafo, desarrollada ms arriba, cuyo mtodo getVecinos retorna todos
los nodos adyacentes a un vrtice obviando aquellos que previamente hayan sido marcados como procesados mediante el mtodo setProcesado.
Veamos cmo crear una instancia de Grafo en funcin de su matriz de adyacencias.
Recordemos que la matriz debe contener la distancia que existe entre dos nodos adyacentes o el valor Integer.MAX_VALUE para aquellos nodos que no lo son.
Esta implementacin del algoritmo de Dijkstra utilizar dos hashtables: una para componer el conjunto solucin (distMin) y otra para mantener las distancias acumuladas
(distAcum). Las keys de estas tablas sern las etiquetas de todos los nodos del grafo y
los valores iniciales deben ser infinito para el conjunto solucin ya que, en definitiva, se
trata de hallar las distancias mnimas y 0 para las distancias acumuladas que no son otra
cosa que acumuladores.
Trabajaremos con una cola donde, inicialmente, encolaremos al vrtice de origen. Luego
iteraremos mientras que la cola no est vaca y en cada una de estas iteraciones desencolaremos un nodo, lo procesaremos y encolaremos a todos sus vecinos.
623
Nota: recordemos que para simplificar la lgica del algoritmo hemos aceptado la restriccin de que los vrtices
del grafo solo podrn estar etiquetados
con valores numricos, consecutivos y
comenzando desde cero.
package libro.cap22.dijkstra.imple;
import
import
import
import
java.util.ArrayList;
java.util.Hashtable;
java.util.LinkedList;
java.util.Queue;
import libro.cap22.Grafo;
import libro.cap22.dijkstra.Dijkstra;
public class DijkstraImpleGreddy implements Dijkstra
{
public Hashtable<Integer, Integer> procesar(Grafo g, int s)
{
// asigno "infinito" a cada posicion de distMin
624
// :
// viene de mas arriba
while(!q.isEmpty())
{
// tomo el primero
int n = q.poll();
g.setProcesado(n);
Al inicio, la distancia acumulada de cada nodo ser cero. Si sumamos la distancia acumulada del nodo n, que acabamos de desencolar, con la distancia existente entre este
y cada uno de sus vecinos podremos determinar si esta suma es menor a la distancia
que, hasta ahora, considerbamos mnima. Si as fuese entonces actualizaremos distMin.
Luego ingresamos a un for para comparar la distancia que existe entre n y cada uno
de sus vecinos.
// :
// viene de mas arriba
// obtengo los vecinos de n
// pido sus vecinos
int t = vecinos.get(i);
int dist = g.getDistancia(n, t) + acum;
int min = distMin.get(t);
if( dist<min )
{
distMin.put(t, dist);
distAcum.put(t, dist);
}
}
}
if(!q.contains(t))
{
q.add(t);
}
return distMin;
625
Dentro del for tomamos el i-simo vecino de n y calculamos la distancia que existe
entre estos nodos sumando la distancia directa ms la distancia que acumula n. Luego
comparamos esta suma con el valor que, por el momento, consideramos como la distancia mnima. Si la nueva distancia mejora a la anterior entonces la guardamos.
Solo queda pendiente el cdigo del mtodo inicializar que utilizamos para asignar
valores iniciales a las hashtables.
// :
// viene de mas arriba
Veamos ahora un programa que reciba por lnea de comandos al vrtice de origen y
muestre por pantalla las menores distancias que existen entre este vrtice y todos los dems.
package libro.cap22.dijkstra;
import java.util.Hashtable;
public class TestDijkstra
{
public static void main(String[] args)
{
int i = Integer.MAX_VALUE;
int[][] mAdy= { {i,
,{6,
,{i,
,{6,
,{i,
,{i,
,{i,
6,
i,
2,
i,
4,
i,
i,
i,
2,
i,
4,
6,
4,
i,
6,
i,
4,
i,
i,
2,
6,
i,
4,
6,
i,
i,
6,
i,
i,
i,
4,
2,
6,
i,
4,
i}
i}
i}
6}
i}
4}
i} };
System.out.println(minimos);
626
Esta implementacin del algoritmo coincide con un enfoque voraz o greddy. Veamos:
La complejidad del algoritmo es cuadrtica y est determinada por los dos ciclos anidados: un for dentro del while.
package libro.cap22.dijkstra.imple;
import java.util.Hashtable;
import libro.cap22.Grafo;
import libro.cap22.dijkstra.Dijkstra;
public class DijkstraImpleDinamica implements Dijkstra
{
public Hashtable<Integer, Integer> procesar(Grafo g, int s)
{
// tabla con la solucion del problema
627
g.setProcesado(s);
// comparo todos contra todos
g.setProcesado(posMin);
for( int j=0; j<g.size(); j++ )
{
if( !g.isProcesado(j) )
{
}
}
return solu;
Probablemente, el lector se haya sorprendido al ver que utilic el mtodo sumar para
obtener la suma entre los valores solu.get(posMin) y g.getDist(posMin, j).
Pues bien, analicemos la siguiente lnea:
628
// :
// viene de mas arriba
Por ltimo, veremos el mtodo menor que retorna la posicin dentro del arraylist que,
temporalmente, representa la menor distancia entre el nodo de origen y el nodo homlogo a dicha posicin. Recordemos que, en el arraylist, las posiciones representan vrtices
y los contenidos representan distancias.
// :
// viene de mas arriba
return pos;
629
package libro.cap22.dijkstra.test;
import java.util.Hashtable;
import java.util.Scanner;
import libro.cap22.Grafo;
import libro.cap22.dijkstra.Dijkstra;
public class Test
{
public static void main(String[] args) throws Exception
{
Scanner scanner = new Scanner(System.in);
int i = Integer.MAX_VALUE;
int[][] mAdy= { {i, 6, i, 6,
,{6, i, 2, i,
,{i, 2, i, 4,
,{6, i, 4, i,
,{i, 4, 6, i,
,{i, i, 4, 2,
,{i, i, i, 6,
i,
4,
6,
i,
i,
6,
i,
i,
i,
4,
2,
6,
i,
4,
i}
i}
i}
6}
i}
4}
i} };
Dijkstra d = (Dijkstra)Class.forName(sClassImple).newInstance();
// invoco el metodo procesar
System.out.println(minimos);
Si queremos que el programa ejecute la implementacin dinmica para calcular las distancias mnimas hacia el vrtice de origen 6 ingresaremos lo siguiente:
Ingrese el vertice de origen: 6
Ingrese implementacion de Dijkstra:
libro.cap22.dijkstra.imple.DijkstraImpleDinamica
{6=2147483647, 5=4, 4=10, 3=6, 2=8, 1=10, 0=12}
Y si queremos utilizar la implementacin greddy, debemos ejecutarlo e ingresar:
Ingrese el vertice de origen: 6
Ingrese implementacion de Dijkstra:
libro.cap22.dijkstra.imple.DijkstraImpleGreddy
{6=2147483647, 5=4, 4=10, 3=6, 2=8, 1=10, 0=12}
630
Dijkstra d = (Dijkstra)Class.forName(sClassImple).newInstance();
El mtodo Class.forName instancia dinmicamente un objeto de la clase indicada en
la cadena sClassImple, ingresada por el usuario. Este es el punto de entrada al mundo
de la programacin por introspeccin que, en Java, se encuentra detrs de la API de
reflection ubicada en el paquete java.lang.reflect.
Si bien, el tema excede el alcance de este libro, el mismo es de suma importancia ya que
permite llevar al extremo los conceptos de abstraccin.
La programacin por introspeccin es la base para el desarrollo de herramientas genricas y frameworks como Hibernate, Spring, Struts, ZK, etc.
6
4
6
4
5
2
4
6
5
2
4
6
Fig. 22.15 rbol de cubrimiento mnimo.
631
package libro.cap22.mst;
import libro.cap22.Grafo;
public interface Mst
{
La estrategia del algoritmo de Prim consiste en armar el rbol recubridor mnimo del grafo
g, que recibimos como parmetro en el mtodo procesar, escogiendo el orden en el
que vamos a incorporar cada uno de sus vrtices en funcin de los valores de las aristas
que los unen.
Supongamos que el vrtice de inicio ser el nodo 0. Entonces, este nodo ser el primero
en incorporarse al rbol ocupando el lugar de la raz. Luego, analizamos cul de todos
sus vecinos se encuentra ubicado a menor distancia. Como en este caso todos sus vecinos son equidistantes, tomaremos cualquiera de ellos, por ejemplo: 1.
4
4
0
Algoritmo de Prim
4
6
5
4
4
6
5
2
4
6
6
4
6
4
5
2
4
6
632
4
6
6
4
6
4
5
2
4
6
4
6
6
4
6
4
5
2
4
6
4
0
6
4
6
4
5
2
4
6
5
2
4
Fig. 22.16 Comparacin del grafo original con el rbol generador mnimo hallado por Prim.
package libro.cap22.mst.imple;
import java.util.ArrayList;
import libro.cap22.Arista;
import libro.cap22.Grafo;
import libro.cap22.mst.Mst;
633
int n = s;
for(int i=0; i<g.size()-1; i++)
{
g.setProcesado(n);
incorporados.add(n);
int
int
int
int
incorporar[] = buscarMinimos(g,incorporados);
desde = incorporar[0]; // nodo ya incorporado
hasta = incorporar[1]; // nodo a incorporar...
dist = g.getDistancia(desde,hasta);
}
}
n = hasta;
return solu;
El mtodo buscarMinimos analiza los vecinos de todos los nodos que contiene el
arraylist incorporados y retorna aquel que se encuentre ubicado a menor distancia.
El mtodo retorna un int[2] donde el primer elemento es el nodo desde el cual nos
conectaremos hacia el prximo nodo que debemos incorporar. Este ltimo ubicado en la
segunda posicin del array.
// :
// viene de mas arriba
634
return ret;
El algoritmo de Kruskal tambin ofrece una solucin al problema de hallar el rbol recubridor mnimo de un grafo, pero en este caso el anlisis pasa por las aristas.
La estrategia consiste en incorporar, una a una, las aristas de menor peso, siempre y
cuando no formen ciclos. Dado que buscamos formar un rbol la cantidad de aristas que
debemos incorporar ser n-1 siendo n la cantidad de vrtices que tiene el grafo original.
Las aristas que agregaremos al conjunto solucin sern (1,2,2), (3,5,2), (1,4,4) y (2,3,4).
La siguiente arista en el conjunto ordenado
es (2,5,4), indicada con una lnea punteada.
Claro que al incorporar esta arista formaramos un ciclo, por esta razn la descartamos y
analizamos las siguientes.
El rbol se completa con las aristas (5,6,4),
(0,1,6) que no forman ciclos y suman la cantidad de n-1 aristas necesarias para unir a todos los vrtices del grafo original.
4
6
6
4
6
4
4
6
4
6
Algoritmo de Kruskal
4
6
4
4
6
El lector podr observar que el rbol que obtuvimos con el algoritmo de Kruskal coincide
con el rbol arrojado por el algoritmo de Prim.
635
// :
}
}
}
if( !arr.contains(a) )
{
arr.add(a);
}
return arr;
// :
Ahora s, veamos la clase MstImpleKruskal que implementa la interface Mst siguiendo estos lineamientos.
package libro.cap22.mst.imple;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import libro.cap22.Arista;
import libro.cap22.Grafo;
import libro.cap22.mst.Mst;
public class MstImpleKruskal implements Mst
{
public Grafo procesar(Grafo g, int s)
{
636
int i=0;
while( !aristas.isEmpty() && i<g.size()-1 )
{
Arista a = aristas.get(0);
// si no hay ciclo la agrego y cuento una arista mas
if( !solu.hayCiclo(a.getN1(),a.getN2()) )
{
solu.addArista(a);
i++;
}
// la remuevo del conjunto de aristas disponibles
}
}
aristas.remove(0);
return solu;
La clase ComparaArista es una inner class. Java permite definir clases dentro de otras
clases con el objetivo de reducir la cantidad de archivos de cdigo fuente. La idea de
incluir una clase adentro de otra es til cuando la clase interna es de uso exclusivo de la
clase contenedora. En nuestro caso, al menos por ahora, la clase ComparaArista solo
es utilizada por la implementacin del algoritmo de Kruskal.
Notemos que el hecho de que una clase est codificada dentro del cuerpo de otra no
afecta para nada la relacin de herencia que pueda existir entre ambas. En el caso de las
clases, MstImpleKruskal y ComparaArista, ambas heredan de Object. Adems,
la primera implementa la interface Mst mientras que la segunda implementa la interface
Comparator. Solo eso.
Volviendo al algoritmo de Kruskal, notemos lo simple que result su implementacin. En
parte, esto se debe a que el mtodo hayCiclo que desarrollamos en la clase Grafo
casi al principio del captulo nos permite determinar si, al conectar dos nodos con una
arista, estaremos o no formando un ciclo en nuestro grafo.
Para terminar, veamos un programa donde el usuario ingresa un vrtice inicial y la implementacin que prefiere utilizar para hallar el rbol de cubrimiento mnimo del grafo que
hemos utilizado en este captulo.
637
package libro.cap22.mst.test;
import java.util.Scanner;
import libro.cap22.Arista;
import libro.cap22.Grafo;
import libro.cap22.mst.Mst;
public class Test
{
public static void main(String[] args) throws Exception
{
Scanner scanner = new Scanner(System.in);
int i = Integer.MAX_VALUE;
int[][] mAdy = { { i, 6, i,
, { 6, i, 2,
, { i, 2, i,
, { 6, i, 4,
, { i, 4, 6,
, { i, i, 4,
, { i, i, i,
6,
i,
4,
i,
i,
2,
6,
i,
4,
6,
i,
i,
6,
i,
i,
i,
4,
2,
6,
i,
4,
i
i
i
6
i
4
i
}
}
}
}
}
}
} };
for(Arista a: solu.getAristas())
{
System.out.println(a);
}
638
22.5 Resumen
En este captulo analizamos la estructura Grafo como una estructura no lineal y recursiva. Observamos la importancia de recordar qu nodos del grafo ya hemos procesado
para no incurrir en loops infinitos.
Los algoritmos sobre grafos son complejos y la mejor forma de abordarlos es encapsulando esta complejidad utilizando clases y objetos, tal como lo hicimos con la clase
Grafo.