Você está na página 1de 20

Grupo CUYS (Como usted ya sabe) | WWW.CUYS.COM.AR Fac. Cs.

Exactas (UNICEN) Hoja 1

Anlisis y Diseos de Algoritmos II Apunte TDA Grafo:


Un Grafo es una estructura de datos no lineal. En principio puede considerarse como una dupla que contiene 2 conjuntos. G = (V, A) V: conjunto de vrtices no vaco, finito A: conjunto de aristas o arcos finito. (puede ser vaco) Pueden ser clasificados de la siguiente manera:

Grafo no dirigido:

Los arcos no tienen una direccin por lo que, el arco 1 2 es el mismo que el 2 1.

Grafo dirigido:

Los arcos tienen una direccin. Si existe el arco 1 2 no necesariamente tiene que existir el 2 1.

Grupo CUYS (Como usted ya sabe) | WWW.CUYS.COM.AR Fac. Cs. Exactas (UNICEN) Hoja 2

Recorrido de un Grafo: Depth First Search (Recorrido en Profundidad): Dado un Grafo G = (V, A) y un vrtice v, perteneciente a V, DFS recorre el grafo desde el primer adyacente no visitado de v. Ejemplo:

Suponiendo que v es 1, el recorrido ser el siguiente:

void DFS(grafo g, int v, bool visitado[N], int padre[N]){ visitado[v] = 1; list<int> Adyacentes = g.devolver_adyacentes(v); list<int>::iterator it = Adyacentes.begin(); while (it != Adyacentes.end()){ if (!visitado[*it]){ padre[*it] = v; DFS(g,*it,visitado,padre); } it++; } } void Recorrido_En_Profundidad(grafo g, int padre[N]){ bool visitado[N]; for (int i = 1; i <= N; i++){ visitado[i] = 0; padre[i] = 0; } for(int vertice = 1; vertice <= N; vertice++) if (!visitado[vertice]) DFS(g,vertice,visitado,padre); }//padre vuelve modificado con el recorrido del DFS

Grupo CUYS (Como usted ya sabe) | WWW.CUYS.COM.AR Fac. Cs. Exactas (UNICEN) Hoja 3

Breadth First Search (Recorrido en Ancho): Dado un Grafo G = (V, A) y un vrtice v, perteneciente a V, BFS recorre el grafo visitando primero todos los adyacentes de v. Ejemplo:

Suponiendo que v es 1, el recorrido ser el siguiente:

void BFS(grafo g, int v, bool visitado[N], int padre[N]){ visitado[v] = 1; list<int> Adyacentes = g.devolver_adyacentes(v); list<int>::iterator it; for(it = Adyacentes.begin(); it != Adyacentes.end(); it++) if (!visitado[*it]){ padre[*it] = v; visitado[*it] = 1; } it = Adyacentes.begin(); while (it != Adyacentes.end()){ if (padre[*it] == v) BFS(g,*it,visitado,padre); it++; } } void Recorrido_En_Ancho(grafo g, int padre[N]){ bool visitado[N]; for (int i = 1; i <= N; i++){ visitado[i] = 0; padre[i] = 0; } for(int vertice = 1; vertice <= N; vertice++) if (!visitado[vertice]) BFS(g,vertice,visitado,padre); }//padre vuelve modificado con el recorrido del BFS

Grupo CUYS (Como usted ya sabe) | WWW.CUYS.COM.AR Fac. Cs. Exactas (UNICEN) Hoja 4

Ciclos en un Grafo:

Ciclos simples: Un grafo tiene ciclos, si cuando lo recorremos por medio de un DFS partiendo de algn vrtice, podemos llegar al vrtice del que partimos. Ejemplo:

#define BLANCO 0 //no ha sido visitado #define GRIS 1 //se ha visitado en la rama (el DFS ya ha pasado por ese lugar) #define NEGRO 2 //se ha visitado por completo el vertice //(no tiene mas adyacentes para ser visitados) bool Hay_Ciclo(grafo g, int vertice, int visitado[N]){ bool found = 0; visitado[vertice] = GRIS; list<int> Adyacentes = g.devolver_adyacentes(vertice); list<int>::iterator it = Adyacentes.begin(); while ((it != Adyacentes.end())&&(!found)){ if (visitado[*it] == BLANCO) found = Hay_Ciclo(g,*it,visitado); else if (visitado[*it] == GRIS) found = 1; it++; } visitado[vertice] = NEGRO; return found; }//found retorna 1, si hay ciclo y 0 en caso de que no lo haya.

Es importante ver que cuando lo marcamos como NEGRO, es cuando volvemos en la recursin.

Grupo CUYS (Como usted ya sabe) | WWW.CUYS.COM.AR Fac. Cs. Exactas (UNICEN) Hoja 5

Ciclo Hamiltoniano: Un grafo tiene ciclo hamiltoniano cuando lo recorremos por medio de un DFS partiendo de algn vrtice y podemos llegar al vrtice del que partimos pasando exactamente 1 vez por cada uno de los vrtices. Ejemplo:

bool todosVisitados(bool visitado[N]){ for(int i = 1; i <= N; i++) if (!visitado[i]) return 0; return 1; } /*cuando se llama a la funcion, vertice y primero, deben ser iguales, el arreglo visitado viene inicializado en 0*/ bool Hay_Ciclo_Hamiltoniano(grafo g, int vertice, bool visitado[N], int primero){ visitado[vertice] = 1; list<int> Adyacentes = g.devolver_adyacentes(vertice); list<int>::iterator it; bool found = 0; for(it = Adyacentes.begin(); it != Adyacentes.end() && !found; it++){ if (!visitado[*it]){ found = Hay_Ciclo_Hamiltoniano(g,*it,visitado,primero); visitado[*it] = 0; } else if (*it == primero) found = todosVisitados(visitado); } return found; }

Grupo CUYS (Como usted ya sabe) | WWW.CUYS.COM.AR Fac. Cs. Exactas (UNICEN) Hoja 6

Ciclo Euleriano: Un grafo tiene ciclo Euleriano cuando lo recorremos por medio de un DFS partiendo de algn vrtice y podemos llegar al vrtice del que partimos pasando exactamente 1 vez por cada uno de los arcos, pudiendo pasar ms de una vez por cada vrtice. Ejemplo:

struct arco{int origen; int destino; int costo; };

void crear_lista_de_aristas((grafo g, list<arco*> &aristas){ list<int> Adyacentes; list<int>::iterator it; for(int i=1; i <= N; i++){ Adyacentes = g.devolver_adyacentes(i); for(it = Adyacentes.begin(); it != Adyacentes.end(); it++) Agregar_Arista(aristas,i, *it, g.costo_entre(i, *it));//usado en kruskal } } bool Camino_no_Usado(int u,int v,list<arco*> aristas){ for(list<arco*>::iterator it = aristas.begin(); it != aristas.end();it++) if (((*it)->origen == u) && ((*it)->destino == v)) return 1; return 0; }

Grupo CUYS (Como usted ya sabe) | WWW.CUYS.COM.AR Fac. Cs. Exactas (UNICEN) Hoja 7

void eliminar(int u,int v, list<arco*> &aristas){ list<arco*>::iterator it = aristas.begin(); list<arco*>::iterator borrar; while(it != aristas.end()){ borrar = it; it++; if (((*borrar)->origen == u) && ((*borrar)->destino == v)) aristas.erase(borrar); } } bool Ciclo_Euleriano(grafo g, list<arco*> &aristas, int vertice, int primero, list<arco*> &camino){ bool found = 0; if (!aristas.empty()){ list<int> Adyacentes = g.devolver_adyacentes(vertice); list<int>::iterator it = Adyacentes.begin(); while(it != Adyacentes.end() && !found){ if (Camino_no_Usado(vertice,*it,aristas)){ Agregar_Arista(camino,vertice, *it, g.costo_entre(vertice, *it)); eliminar(vertice,*it,aristas); found = Ciclo_Euleriano(g, aristas, *it, primero, camino); if (!found){ Agregar_Arista(aristas,vertice, *it, g.costo_entre(vertice, *it)); camino.pop_back(); } } it++; } } else if (vertice == primero) found = 1; return found; }

Grupo CUYS (Como usted ya sabe) | WWW.CUYS.COM.AR Fac. Cs. Exactas (UNICEN) Hoja 8

Orden Topolgico de un Grafo: Dado un grafo dirigido acclico G = (V, A), para todo par de vrtices v, w perteneciente a V, tal que v w entonces v debe aparecer primero en la secuencia. Para armar esta secuencia solo basta con anotar en una pila los vrtices completamente explorados por el DFS. Ejemplo:

El orden topolgico ser el siguiente:

void DFS(grafo g, int v, bool visitado[N], list<int> &pila){ visitado[v] = 1; list<int> Adyacentes = g.devolver_adyacentes(v); list<int>::iterator it = Adyacentes.begin(); while (it != Adyacentes.end()){ if (!visitado[*it]){ DFS(g,*it,visitado,pila); pila.push_front(*it); } it++; } } void Orden_Topologico(grafo g, list<int> &pila){ bool visitado[N]; pila.clear(); for (int i = 1; i <= N; i++) visitado[i] = 0; for(int vertice = 1; vertice <= N; vertice++) if (!visitado[vertice]){ DFS(g,vertice,visitado,pila); pila.push_front(vertice); } }

Grupo CUYS (Como usted ya sabe) | WWW.CUYS.COM.AR Fac. Cs. Exactas (UNICEN) Hoja 9

Componentes Fuertemente Conectadas de un Grafo: Dado un grafo dirigido G = (V, A), el conjunto mximo de vrtices V' contenido en V, tal que para cada par de vrtices u y v en V', existe un camino tanto de u v como de v u es lo que se conoce como componentes fuertemente conectadas. Para armar este algoritmo solo basta con seguir los siguientes pasos. 1- Aplicar DFS y numerar los nodos en postorden en la vuelta de la recursin del Grafo G. (utilizamos el mismo algoritmo que el orden topolgico para generar la pila, pero es importante destacar que esta pila no nos devuelve el orden topolgico si el grafo es cclico) 2- Construir el Grafo invertido GR. 3- Hacer el recorrido en profundidad en el orden de la pila. Ejemplo: Grafo G:

1-Pila:

2-Grafo GR:

Grupo CUYS (Como usted ya sabe) | WWW.CUYS.COM.AR Fac. Cs. Exactas (UNICEN) Hoja 10

3-Componentes Fuertemente Conectadas:

void invertir_grafo(grafo g, grafo &GR){ list<int> Adyacentes; list<int>::iterator it; for(int vertice = 1; vertice <= N; vertice++){ Adyacentes = g.devolver_adyacentes(vertice); for(it = Adyacentes.begin(); it != Adyacentes.end(); it++) GR.agregar_arco(*it,vertice,g.costo_entre(vertice,*it)); Adyacentes.clear(); } } void CompFuertementeConectadas(grafo GR, list<int> pila){ list<int> compF; bool visitado[N]; for (int i = 1; i <= N; i++) visitado[i] = 0; for(list<int>::iterator it = pila.begin(); it != pila.end(); it++) if (!visitado[*it]){ DFS(GR,*it,visitado,compF); compF.push_front(*it); mostrar_lista(compF); compF.clear(); } } void MostrarCFC(grafo g){ list<int> pila; int padre[N]; grafo GR(N); Orden_Topologico(g,pila); invertir_grafo(g,GR); CompFuertementeConectadas(GR,pila); }

Grupo CUYS (Como usted ya sabe) | WWW.CUYS.COM.AR Fac. Cs. Exactas (UNICEN) Hoja 11

Bsquedas de Caminos Mas Cortos:

Algoritmo de Dijkstra: Devuelve un arreglo con los costos de los caminos mas cortos entre un vrtice v dado y el resto. Utiliza tcnica Greedy, incorporando un vrtice a un conjunto S cuya distancia al vrtice dado sea la mas corta posible. Ademas utiliza otro arreglo padre, donde se guarda el camino seguido para obtener el menor costo. Ejemplo:

Si tomamos v igual a 4 entonces:

Grupo CUYS (Como usted ya sabe) | WWW.CUYS.COM.AR Fac. Cs. Exactas (UNICEN) Hoja 12 bool Pertenece(list<int> L, int valor){ list<int>::iterator i = L.begin(); while (i != L.end() && (valor!=*i)) i++; if (valor==*i) return 1; return 0; } int Minimo(int D[N], list<int> solucion){ int valor_minimo = INF; int indice_minimo = INF; for(int i=1 ; i<=N ; i++) if (!Pertenece(solucion,i)) if (D[i] < valor_minimo){ valor_minimo = D[i]; indice_minimo = i; } return indice_minimo; } int Min(int D[N],int Matriz[N][N],int posmin,int v){ if (D[v]<(Matriz[posmin][v] + D[posmin])) return D[v]; return (Matriz[posmin][v] + D[posmin]); } void Dijkstra(int Matriz[N][N], int D[N], int vertice, int P[N]){ list<int> S; S.push_back(vertice); for(int i = 1; i <= N; i++){ D[i] = Matriz[vertice][i]; if ((D[i] != INF) && (D[i] != 0)) P[i] = vertice; else P[i] = -1; } for(int t = 1; t <= N; t++){ int posmin = Minimo(D,S); S.push_back(posmin); for(int v=1; v <= N; v++) if (!Pertenece(S,v)){ int guardovalor = Min(D,Matriz,posmin,v); if (D[v] != guardovalor){ D[v] = guardovalor; P[v] = posmin; } } } }

Grupo CUYS (Como usted ya sabe) | WWW.CUYS.COM.AR Fac. Cs. Exactas (UNICEN) Hoja 13

Algoritmo de Floyd: Devuelve una Matriz A[N][N] con los costos de los caminos mas cortos entre todo par de vrtices, a partir de una matriz de costos C[N][N]. Donde C[i][j] es el costo de i j = j i, y C[i][i] = 0. Utiliza Programacin dinmica. Ejemplo: Grafo:

C[N][N]:

A[N][N]:

void Floyd(int C[N][N], int A[N][N]){ for(int i=1 ; i<=N ; i++) for(int j=1 ; j<=N ; j++) A[i][j]=C[i][j];//copio la matriz original en A for(int k=1; k<=N; k++) for(int i=1; i<=N; i++) for(int j=1; j<=N; j++) if((A[i][k] + A[k][j])< A[i][j]) A[i][j] = A[i][k] + A[k][j]; }

Grupo CUYS (Como usted ya sabe) | WWW.CUYS.COM.AR Fac. Cs. Exactas (UNICEN) Hoja 14

rbol de Recubrimiento Mnimo: Dado un grafo conexo no dirigido G = (V, A), el problema es encontrar un grafo tal que la suma de los costos de las aristas sea mnima, y no queden vrtices sin conectar. Para esto se presentan 2 algoritmos.

Algoritmo de Prim: Este algoritmo, utiliza un conjunto solucin S, donde ya estn los vrtices cuyo costo es mnimo, y un conjunto de vrtices V en el cual se encuentran todos los vrtices. La idea principal es, elegir un vrtice cualquiera de V, agregarlo a S, seleccionar un vrtice de V cuyo costo con cualquiera de los vrtices de S sea mnimo, y agregarlo al conjunto solucin hasta que me quede sin vrtices. Siempre teniendo en cuenta de no repetir vrtices. Ejemplo:

Si tomamos el vrtice 1 entonces:

Grupo CUYS (Como usted ya sabe) | WWW.CUYS.COM.AR Fac. Cs. Exactas (UNICEN) Hoja 15

struct arco{int origen; int destino; int costo; }; /*en h se devuelve el grafo de minimo costo. h viene inicializado unicamente con los vertices, los arcos se iran agregando durante la ejecucion del algoritmo*/ void Prim(grafo g, grafo &h){ arco min; list<int> s,v; v = g.devolver_vertices(); s.push_back(v.front()); v.pop_front(); list<int>::iterator its; list<int>::iterator itv; while (!v.empty()){ min.costo = INFINITO; for(its = s.begin(); its != s.end(); its++) for(itv = v.begin(); itv != v.end(); itv++) if (g.costo_entre(*its,*itv) < min.costo){ min.costo = g.costo_entre(*its,*itv); min.origen = *its; min.destino = *itv; } s.push_back(min.destino); v.remove(min.destino); h.agregar_arco(min.origen,min.destino,min.costo); } }

Grupo CUYS (Como usted ya sabe) | WWW.CUYS.COM.AR Fac. Cs. Exactas (UNICEN) Hoja 16

Algoritmo de Kruskal: Este algoritmo, utiliza un conjunto de aristas A donde se van guardando las aristas de mnimo costo, y una fila de aristas fila, en el cual se encuentran todas las aristas ordenadas ascendentemente segn costo. La idea principal es, elegir la arista de mnimo costo perteneciente a fila y agregarla a A, siempre que no se genere ciclo, hasta que la cantidad de aristas de A sea la cantidad de vertices -1. Ejemplo:

Grupo CUYS (Como usted ya sabe) | WWW.CUYS.COM.AR Fac. Cs. Exactas (UNICEN) Hoja 17 struct arco{int origen; int destino; int costo; }; void insertarOrdenado(list<arco*> &fila, arco * nuevo){ list<arco*>::iterator it = fila.begin(); while((it != fila.end()) && (((*it)->costo) < (nuevo->costo))) it++; fila.insert(it,1,nuevo);//inserta una posicion atras del iterador } void cargarFila(grafo g, list<arco*> &fila){ list<int> Adyacentes; list<int>::iterator it; for(int i=1; i <= N; i++){ Adyacentes = g.devolver_adyacentes(i); for(it = Adyacentes.begin(); it != Adyacentes.end(); it++){ arco * nuevo = new arco; nuevo->origen = i; nuevo->destino = *it; nuevo->costo = g.costo_arco(i,*it); insertarOrdenado(fila,nuevo); } } } void Agregar_Arista(list<arco*> & A, int u, int v, int c){ arco * nuevo = new arco; nuevo->origen = u; nuevo->destino = v; nuevo->costo = c; A.push_back(nuevo); }

Grupo CUYS (Como usted ya sabe) | WWW.CUYS.COM.AR Fac. Cs. Exactas (UNICEN) Hoja 18

/*en h se devuelve el grafo de minimo costo. h viene inicializado unicamente con los vertices, los arcos se iran agregando durante la ejecucion del algoritmo*/ void Kruskal(grafo g, grafo &h){ list<arco*> fila; list<arco*> A; cargarFila(g,fila); while (A.size() < N-1){ int u = fila.front()->origen; int v = fila.front()->destino; int c = fila.front()->costo; fila.pop_front(); //existe_camino es utilizado para verificar que no se genera ciclo. if (!h.existe_camino(u,v)){ h.agregar_arista(u,v,c); Agregar_Arista(A,u,v,c); } } }

Comentario: Tanto para Prim como para Kruskal, tener en cuenta que el agregar_arco, al ser un grafo no dirigido, tendra que agregar un arco tanto de origen a destino, como de destino a origen.

Grupo CUYS (Como usted ya sabe) | WWW.CUYS.COM.AR Fac. Cs. Exactas (UNICEN) Hoja 19

Problemas NP:

Algoritmo de Coloreo: El problema consiste en asignar un color a cada vrtice de un grafo de manera que vrtices adyacentes, no posean el mismo color y utilizando la menor cantidad de colores posibles. Para este problema se plantea un algoritmo heurstico, que no siempre da la mejor solucin, pero en general es ptimo. Ejemplo:

bool pueda_colorear(grafo g, int vertice, int color, int Colores[N]){ list<int> Adyacentes = g.devolver_adyacentes(vertice); list<int>::iterator it = Adyacentes.begin(); while ((it != Adyacentes.end())&&(Colores[*it] != color)) it++; if (it == Adyacentes.end()) return 1; return 0; } /*Colores se inicializa en un valor especial*/ void NP_Coloreo(grafo g, int Colores[N]){ for (int vertice = 1; vertice <= N; vertice++){ int color = 1; while (!pueda_colorear(g, vertice, color, Colores)) color++; Colores[vertice] = color; } }

Para este ejemplo el color 1 sera el Rojo, el 2 Verde y el 3 Celeste.

Grupo CUYS (Como usted ya sabe) | WWW.CUYS.COM.AR Fac. Cs. Exactas (UNICEN) Hoja 20

Vertex Cover: Dado un G = (V, A) no dirigido, el problema es encontrar un conjunto de tamao mnimo C perteneciente a V, tal que C cubra todos los arcos A. Para este problema se plantea un algoritmo heurstico, que no siempre da la mejor solucin, pero en general es ptimo. Ejemplo:

El Conjunto mnimo de vrtices que cubre todos los arcos es 4,1,2.


void eliminar_arcos_incidentes(list<arco*> &l, int vertice){ list<arco*>::iterator it = l.begin(); list<arco*>::iterator borrar; while(it != l.end()){ borrar = it; it++; if (((*borrar)->origen == vertice) || ((*borrar)->destino == vertice)) l.erase(borrar); } } void vertex_cover(grafo g, list<int> & C){ list<arco*> l; crear_lista_de_aristas(g,l); list<arco*>::iterator it = l.begin(); while(!l.empty()){ //elijo un vertice arbitrario (tomo el 1) C.push_front((*it)->origen); eliminar_arcos_incidentes(l,(*it)->origen); it = l.begin(); } }//se podria mejorar ordenando l de mayor a menor por cantidad de incidencia.

Você também pode gostar