La clase Grafo public class Grafo { private private private private Tema 9: GRAFOS Segunda Parte Estructuras de Datos y Algoritmos Curso 2002/03 static final int TAMANYO_INICIAL=10; Vertice tabla []; int numVertices; TablaHash diccio; public Grafo() { ... } public insArista (String orig,String dest,int cost) { ... } public String toString() { ... } } Grafos. EDA. Curso 2002/03 4 Algoritmos de recorrido de grafos La clase Vertice public class Vertice { String nombre; //el nombre del vértice Lista ady; // la lista de vértices adyacentes (aristas) public Vertice(String v) { nombre=v; ady=new Lista(); Lista: } public void insertar(Object x); toString() //public la posiciónString del punto de interés continua { apuntando al mismo elemento public void borrar() throws DesbordamientoInferior; return ady.toString(); //}borra el elemento que ocupa la posición del punto de interés (queda marcando al siguiente) }public Object recupera(); public boolean esVacia(); public void inicio(); public void fin(); public void siguiente() throws PosicionIncorrecta; public boolean esFin(); public String toString(); Grafos. EDA. Curso 2002/03 2 ] DFS: recorrido en profundidad (Depth First Search), generalización del recorrido en preorden de un árbol. ] BFS: recorrido en anchura (Breath First Search), generalización del recorrido por niveles de un árbol. 3 Grafos. EDA. Curso 2002/03 4 Algoritmo de Recorrido en profundidad Recorrido en profundidad (DFS) ] Explora sistemáticamente las aristas del grafo de forma que primero se visitan los vértices adyacentes a los visitados más recientemente. Así se va “profundizando” en el grafo. ] Algoritmo de recorrido en profundidad: Método privado recursivo DFS(v) ordenRecorrido++; R[v]=ordenRecorrido; wAdyacentes(v): Si (R[w]==0) entonces DFS(w) \ Contador (ordenRecorrido) \ Vector de naturales (R) para [ “marcar” los vértices ya visitados (R[v]>0) y [ almacenar el orden de recorrido. R[v]: orden en el que se visita el vértice v. Grafos. EDA. Curso 2002/03 5 7 2 6 3 1 8 4 5 9 1 0 1/2,3,4,5 1 2 3/2,6,8 6/7 7/2 8/6 4/3,8 5/9/5,8 R 1 Grafos. EDA. Curso 2002/03 2 0 2 2 3 0 3 3 Grafos. EDA. Curso 2002/03 6 Recorrido en profundidad en Java Ejemplo: Recorrido DFS Nodos v/w Recorrido en Profundidad ordenRecorrido=0; vV: R[v]=0; vV: Si (R[v]==0) entonces DFS(v) R mantiene el orden de recorrido finRP 4 0 7 7 R 5 0 8 8 6 0 4 4 7 0 5 5 8 0 6 6 int R[]; int OrdenRecorrido; public void RecorridoEnProfundidad () { R=new int[numVertices]; OrdenRecorrido=1; for (int i=0;i<numVertices;i++) R[i]=0; for (int i=0; i<numVertices; i++) if (R[i]==0) DFS(i); } private void DFS (int i) { R[i]=OrdenRecorrido++; Lista b=tabla[i].ady; b.inicio(); while (!b.esFin()) { Arista w=(Arista) b.recupera(); if (R[w.dest]==0) DFS(w.dest); b.siguiente(); } } 9 0 9 9 7 Grafos. EDA. Curso 2002/03 8 Recorrido en anchura (BFS) Ejercicio: ] Hacer una traza del recorrido en profundidad sobre el grafo siguiente: ] Explora sistemáticamente las aristas del grafo de forma que primero se visitan los vértices más “cercanos” al que estamos explorando. ] Algoritmo de recorrido en anchura: \ V = { V0, V1,V2,V3,V4,V5,V6 } \ E = { (VO,V1), (V0,V3), (V1,V3), (V1,V4), (V2,V0), (V2,V5), (V3,V4), (V3,V5), (V3,V6), (V4,V6), (V6,V5) } \ Contador (OrdenRecorrido) \ Vector de naturales (R) para [ “marcar” los vértices ya visitados (R[v]>0) y [ almacenar el orden de recorrido. \ Cola (Q) para gestionar los vértices no visitados public interface cola { void insertar (Object x); Object quitarPrimero () throws DesbordamientoInferior; Object primero () throws DesbordamientoInferior; boolean esVacia(); void vaciar(); } Grafos. EDA. Curso 2002/03 Grafos. EDA. Curso 2002/03 9 Algoritmo de Recorrido en anchura Ejemplo: Recorrido BFS Recorrido en Amplitud OrdenRecorrido=0; vV: R[v]=0; Q=new ColaEnlazada(); vV: Si (R[v]==0) entonces BFS(v) R mantiene el orden de recorrido 7 2 Método privado para el recorrido en Amplitud BFS(v) OrdenRecorrido++; R[v]=OrdenRecorrido; Q.insertar(v); mientras (!Q.esVacia()) { u=Q.quitarprimero(); wAdyacentes(u): Si R[w]==0 { OrdenRecorrido++; R[w]=OrdenRecorrido; Q.insertar(w) } Grafos. EDA. Curso 2002/03 10 6 3 1 8 4 5 11 9 Nodos v u w 1 1 1 1 2 3 4 5 2 3 2 6 8 4 3 8 5 6 7 8 6 7 9 9 5 8 1 R 2 0 2 2 Grafos. EDA. Curso 2002/03 3 0 3 3 4 0 4 4 R 5 0 5 5 Q 6 0 6 6 7 0 8 8 8 0 7 7 9 0 9 9 <1> <> <2> <2,3> <2,3,4> <2,3,4,5> <3,4,5> <4,5> -<4,5,6> <4,5,6,8> <5,6,8> --<6,8> <8> <8,7> <7> -<> <9> <> --- 12 Recorrido en anchura en java public void RecorridoEnAmplitud() { R=new int[numVertices];OrdenRecorrido=1;q=new colaVec(); for (int i=0; i<numVertices; i++) R[i]=0; for (int i=0; i<numVertices; i++) if (R[i]==0) BFS(i); } private void BFS (int v) { R[v]=OrdenRecorrido++; q.insertar(new Integer(v)); while (!q.esVacia()) { Integer u= (Integer) q.quitarPrimero(); Lista b=tabla[u.intValue()].ady; b.primero(); while (!b.esFin()) { Arista w=(Arista) b.recupera(); if (R[w.dest]==0) { R[w.dest]=OrdenRecorrido++; q.insertar(new Integer(w.dest)); } b.siguiente(); } } } Grafos. EDA. Curso 2002/03 Ejercicio: ] Hacer una traza del recorrido en amplitud sobre el grafo siguiente: \ V = { V0, V1,V2,V3,V4,V5,V6 } \ E = { (VO,V1), (V0,V3), (V1,V3), (V1,V4), (V2,V0), (V2,V5), (V3,V4), (V3,V5), (V3,V6), (V4,V6), (V6,V5) } 13 14 Algoritmo de Ordenación Topológica Ordenación topológica en GDA ] Aplicaciones: \ Representación de las fases de un proyecto en un GDA \ Evaluación de atributos en la fase de análisis semántico de un compilador ] Algoritmo para el recorrido según la OT \ Utilizar el recorrido en profundidad para ordenar los vértices según un orden (parcial) “½“ tal que u,vV, si (u,v)E, u ½ v. \ Basta ir anotando en una pila global (P) los vértices completamente explorados por DFS. Grafos. EDA. Curso 2002/03 Grafos. EDA. Curso 2002/03 OTP ordenRecorrido=0; vV: R[v]=0; p= new pilaVec(); vV: Si (R[v]==0) entonces DFST(v) ; Método privado recursivo DFST(v) ordenRecorrido++; R[v]=ordenRecorrido; wAdyacentes(v): Si (R[w]==0) entonces DFST(w); p.apilar(v); 15 Grafos. EDA. Curso 2002/03 16 Ejercicio Ejemplo: Ordenación topológica Nodos v/w 7 2 6 3 1 8 4 5 9 9 1 5 4 1 0 1 1/2,3,4,5 2 3/2,6,8 6/7 7/2 --8/6 --4/3,8 5/--9/5,8 3 8 2 0 2 - 6 3 0 3 - 4 0 7 - R 5 0 8 - 7 P 6 0 4 - 2 7 0 5 - 8 0 6 - 9 0 9 <> <> <2> <2> <2> <7,2> <6,7,2> <8,6,7,2> <3,8,6,7,2> <4,3,8,6,7,2> <5,4,3,8,6,7,2> <1,5,4,3,8,6,7,2> <9,1,5,4,3,8,6,7,2> Grafo ordenado topológicamente Grafos. EDA. Curso 2002/03 Grafos. EDA. Curso 2002/03 17 BÚSQUEDA DE CAMINOS MÍNIMOS EN GRAFOS 18 Problema: dado el siguiente grafo, encontrar el camino más corto (medido por el número de aristas) desde su Vértice v2 a cualquier otro vértice ] Problema del camino mínimo sin pesos \ La longitud del camino sin pesos mide el número de aristas \ Encontrar el camino más corto (medido por el número de aristas) desde un cierto vértice O a cualquier otro vértice ] Problema del camino mínimo con pesos positivos (algoritmo de Dijkstra) v0 19 1 v1 v4 v3 v2 v5 \ La longitud del camino con pesos es la suma de los costes de las aristas del camino \ Las aristas tienen costes no negativos \ Se trata de encontrar el camino más corto (medido con su coste total) desde el vértice origen al resto de vértices \ El problema anterior es un particular de éste, en el que las aristas tiene coste 1 Grafos. EDA. Curso 2002/03 Implementación del algoritmo de Ordenación topológica en Java v6 Problema0: encontrar el Camino más corto desde v2a v2 distancia mínima Solución0: //la no o longitud 0 de //lahay distancia mínima devv22aavv22es es00 distanciaMin[vOrigen] distanciaMin[vOrigen]= =0; 0;; Grafos. EDA. Curso 2002/03 20 Problema: dado el siguiente grafo, encontrar el camino más v0 corto (medido por el número de aristas) desde su Vértice v2 a cualquier otro vértice v0 v5 v5 v4 v3 v2 v4 v3 v2 distanciaM[vOrigen] distanciaM[vOrigen]= = 0; 0;; v1 v1 distanciaM[vOrigen] distanciaM[vOrigen]== 0; 0;; distanciaMin[primerVAdyacente(vOrigen)] distanciaMin[primerVAdyacente(vOrigen)]==1; 1;; ... ... distanciaMin[últimoVAdyacente(vOrigen)] distanciaMin[últimoVAdyacente(vOrigen)]==1; 1;; v6 Problema2: encontrar el Camino más corto desde v2 a los Vértices más cercanos a los Vértices adyacentes a v2 v6 Solución : los Vértices más cercanos son2sus ////distancia mínima es (v adyacentes no tiene) 2distancia mínimade devv00aavv1-v 1-v33 es 2 (v55 no tiene) ” o longitud (mínima) 2 están “a 2 Aristas de v distanciaMin[primerVAdyacente(primerVAdyacente(vOrigen)] ==2; 2 distanciaMin[primerVAdyacente(primerVAdyacente(vOrigen)] 2;; Problema1: encontrar el Camino más corto desde v2 a sus Vértices más cercanos ¿ Cómo ////lala distancia mínima de a v sus /v adyacentes, es 1 Solución1: los Vértices más cercanos distancia mínima devv2son 2 a v00/v55 es 1 están “a 1 Arista de v2” olongitud (mínima) distanciaMin[primerVAdyacente(vOrigen)] = distanciaMin[primerVAdyacente(vOrigen)] =1; 1;1; ... ... distanciaMin[últimoVAdyacente(vOrigen)] distanciaMin[últimoVAdyacente(vOrigen)]= =1; 1;; Grafos. EDA. Curso 2002/03 Usar una recordar Cola dónde seguir ? 21 distanciaMin[segundoVAdyacente(primerVAdyacente(vOrigen)] distanciaMin[segundoVAdyacente(primerVAdyacente(vOrigen)]==2; 2;; ... ... distanciaMin[últimoVAdyacente(primerVAdyacente(vOrigen)] distanciaMin[últimoVAdyacente(primerVAdyacente(vOrigen)]==2; 2;; ... ... distanciaMin[primerVAdyacente(últimoVAdyacente(vOrigen)] distanciaMin[primerVAdyacente(últimoVAdyacente(vOrigen)]==2; 2;; distanciaMin[segundoVAdyacente(últimoVAdyacente(vOrigen)] distanciaMin[segundoVAdyacente(últimoVAdyacente(vOrigen)]==2; 2;; ... ... distanciaMin[últimoVAdyacente(últimoVAdyacente(vOrigen)] ==2; Grafos. EDA. Curso 2002/03 distanciaMin[últimoVAdyacente(últimoVAdyacente(vOrigen)] 2;; 22 Resolución del problema de caminos mínimos sin pesos Problema: dado un grafo, encontrar el camino más corto (medido por el número de aristas) desde un Vértice dado, origen, a cualquier otro vértice CaminoMinimoSinPeso(origen) Reformulación de Problema: Recorrido en Anchura o Breath First Search (BFS) desde el origen, en la que el vector R pasa a ser distanciaMin: ¾ distanciaMin[i] representa la distancia mínima de origen al Vértice i ¾ si distanciaMin[i] == v, el Vértice i NO se ha visitado aún por lo que la distancia mínima de origen al Vértice i es la máxima posible ¾ distanciaMin[origen] == 0 ¾ si distanciaMin[i] > 0, el Vértice i SÍ se ha visitado, desde un cierto vAnterior/ vV: distanciaMin[v]=f ; Coste del algoritmo: O(|E|) distanciaMin[origen] = 0 ; q.insertar(origen); while ( !q.esVacia()) { v=q.quitarprimero() ; Lineal con el tamaño del grafo (número de arcos) wAdyacentes(v): if (distanciaMin[w] == f) { distanciaMin[w] = distanciaMin[v] + 1 ; q.insertar(w); } distanciaMin[i]=distanciaMin[vAnterior] + 1 Grafos. EDA. Curso 2002/03 23 Grafos. EDA. Curso 2002/03 24 public void CaminoMinimoSinPeso(int origen){ Problema: dado el siguiente grafo, encontrar el camino más distanciaMin = new int[numVertices]; for (int i=0 ; i<=numVertices ; i++) distanciaMin[i]=INFINITO ; q=new ColaVec(); corto (medido por el número de aristas) desde su Vértice v2 a cualquier otro vértice v0 v5 0 2 2 2 0 0 5 v4 v3 v2 Vertice’s v1 v6 Comprobar == inf evita que distanciaMin[3] pase a valer 3 |q |q |q |q |q |q = 2| q = =0 = 0, 5 = 5, 1 = 5, 1, 3 = 1, 3 1|q = 3, 4 v 1 1 DistanciaMin 1 2 3 4 5 v 0vvv - - - - - - - - 1 2 - - - - - 2 - - - - - - - - 3- - - - - - - - - 2 0 2 3 - 1 distanciaMin[origen]=0 ; q.insertar(new Integer(origen)); try { while ( !q.esVacia()) { int v=((Integer)q.quitarprimero())..intValue() ; 6 v - Lista b=tabla[v].ady; b.inicio(); while ( !b.esFin()) { Arista a = (Arista) b.recupera(); int w = a..dest(); if (distanciaMin[w] == INFINITO ) { distanciaMin[w] = distanciaMin[v] + 1 ; q.insertar(new Integer(w)); } b.siguiente(); } } catch (DesbordamientoInferior e) {// No puede ocurrir } } distanciaMin[4] = distanciaMin[3] + 1 Grafos. EDA. Curso 2002/03 Grafos. EDA. Curso 2002/03 25 Encontrar no solo el coste sino también el camino 26 Mostrar por pantalla el camino Usar un vector para guardar el camino: P[i] es el vértice anterior a i en el camino mínimo void imprimirCamino(int destino) { if (P[destino]!=-1) { imprimirCamino(P[destino]); CaminoMinimoSinPeso(origen) vV: distanciaMin[v]=f ; vV: P[v]=-1 ; } distanciaMin[origen] = 0 ; q.insertar(origen); while ( !q.esVacia()) { v=q.quitarprimero() ; wAdyacentes(v): System.out.println(tabla[destino].nombre); } if (distanciaMin[w] == f) { P[w]=v; distanciaMin[w] = distanciaMin[v] + 1 ; q.insertar(w); } Grafos. EDA. Curso 2002/03 27 Grafos. EDA. Curso 2002/03 28 Resolución del problema de caminos mínimos con pesos Algoritmo de Dijkstra Algoritmo de Dijkstra ] Cambio en el ajuste del vector de distancias Dijkstra (origen) \ El valor distanciaMin[v] sólo se modificaba una vez \ Ahora el algoritmo debe decidir si para llegar a un cierto vértice w es mejor pasar por v o no: distanciaMin[w]=min(distanciaMin[w], distanciaMin[v]+coste(v,w)) ] Cambio en la selección del vértice a tratar \ Seleccionar el vértice más próximo ahora no es el tratado más recientemente sino el de menor coste vV: distanciaMin[v]=f ; Coste del algoritmo: O(|V|2) Mejora: Usar una Cola de Prioridad distanciaMin[origen] = 0 ; para representar C C={V}; //todos los vértices por tratar while ( C!=) { v=el elemento de C con menor valor de distanciaMin; Eliminar v de C; wAdyacentes(v): if (distanciaMin[w]>distanciaMin[v]+coste(v,w)) { [ La estrategia NO es usar una cola (First-In First-Out) [ El primero visitado no es el más próximo necesariamente distanciaMin[w] = distanciaMin[v] + coste(v,w) ; \ En general utilizaremos un conjunto C para guardar los vértices no tratados: al principio son todos y en cada iteración se elimina uno, aquel que tiene el valor de distanciaMin menor. Grafos. EDA. Curso 2002/03 } Grafos. EDA. Curso 2002/03 29 Complejidad temporal (n=|V|) 30 Complejidad temporal (n=|V|) Matriz de adyacencias y C una lista Dijkstra (origen) Listas de adyacencias y C un minHeap Dijkstra (origen) vV: distanciaMin[v]=f ; vV: distanciaMin[v]=f ; distanciaMin[origen] = 0 ; 4(n) C={V}; //todos los vértices por tratar n-1 iteraciones while ( C!=) { v=el elemento de C con menor valor de distanciaMin; Eliminar v de C; n 1 n(n 1) distanciaMin[origen] = 0 ; 4(n) C={V}; //todos los vértices por tratar n-1 iteraciones while ( C!=) { v=el elemento de C con menor valor de distanciaMin; Eliminar v de C; n 1 wAdyacentes(v): T ( n) ¦ k (n i ) i 1 k if (distanciaMin[w]>distanciaMin[v]+coste(v,w)) { 2 4( n 2 ) wAdyacentes(v): ¦ gradoG x log(n i) 4(| E | log n) i 1 if (distanciaMin[w]>distanciaMin[v]+coste(v,w)) { distanciaMin[w] = distanciaMin[v] + coste(v,w) ; distanciaMin[w] = distanciaMin[v] + coste(v,w) ; } En la iteración i: |C| Seleccionar Eliminar Grafos. EDA. Curso 2002/03 n-i 4(n-i) 4(n-i) T ( n) } Bucle 4(n-i) 31 En la iteración i: |C| Seleccionar y Eliminar n-i 4(log n-i)Grafos. EDA. Curso 2002/03 Bucle 4(gradoG log (n-i)) 32 Implementación del algoritmo de Dijkstra La clase ElementoHeap ] Opción 1: public class ElementoHeap implements Comparable{ int dest; // vértice w int coste; // DistanciaMin[w] \ Estructurar D como un minHeap \ Implementar C como un vector de bits \ Utilizar un vector para marcar la posición de cada vértice en el heap static ElementoHeap infNeg=new ElementoHeap(); ElementoHeap() {this(0);} ElementoHeap (int d) { this(d,0);} ElementoHeap (int d, int c) { dest=d; coste=c;} ] Opción 2: \ Usar una cola de prioridad implementada como un minHeap en la que cada elemento es un par (vértice, DistanciaMin[vértice]) \ Usar un vector para marcar los vértices ya visitados (desencolados). De esta forma se trata las posibles repeticiones de vértices en la cola de prioridad public boolean menorQue (Comparable otro) { return (coste< ((ElementoHeap)otro).coste); } public int compareTo (Object otro) { return coste < ( (ElementoHeap)otro).coste ? -1 : coste > ( (ElementoHeap)otro).coste ? 1 : 0; } [ Seguro que el vértice se trata con el menor valor de DistanciaMin [ Ya no se vuelve a tratar porque se marca Grafos. EDA. Curso 2002/03 } Implementación del algoritmo de Dijkstra 34 Ejercicios: public void dijkstra(int origen){ distanciaMin = new int[numVertices]; for (int i=0 ; i<=numVertices ; i++) distanciaMin[i] = INFINITO ; qPrioridad = new MonticuloBinario(new new ElementoHeap()); distanciaMin[vOrigen] = 0; qPrioridad.insertar(new ElementoHeap(origen, 0)); try { while ( !q.esVacia()) { ElementoHeap par = ((ElementoHeap)q.eliminarMin()); v = par..dest(); if (desencolados[v] == 0) { desencolados[v] = 1; Lista b= tabla[v].ady(); b.inicio(); while ( !b.esFin()) { Arista a = (Arista) b.recupera(); int w = a..dest; int costeVW = a..coste(); if ( distanciaMin[w] > distanciaMin[v] + costeVW) { distanciaMin[w] = distanciaMin[v] + costeVW ; qPrioridad.insertar(new ElementoQ(w, distanciaMin[w] )); } b.siguiente(); } } catch (DesbordamientoInferior e) {// No puede ocurrir } } Grafos. EDA. Curso 2002/03 Grafos. EDA. Curso 2002/03 33 35 1.- Hacer una traza del algoritmo de Dijkstra sobre este grafo, tomando como 2 vértice origen v2 v v 4 v2 5 0 1 4 1 2 8 v5 v3 1 4 2 v6 10 v4 6 2.- Implementar en Java las operaciones sobre grafos, incluyendo recorridos y obtención de caminos siguiendo los algoritmos vistos en clase 3.- Implementar en Java las operaciones sobre grafos, incluyendo recorridos y obtención de caminos bajo el supuesto de que los grafos con los que vamos a trabajar sean densos Grafos. EDA. Curso 2002/03 36