SlideShare ist ein Scribd-Unternehmen logo
1 von 43
Downloaden Sie, um offline zu lesen
1 
Profesor Leopoldo Silva Bijit 20-01-2010 
Capítulo 10 
Grafos. 
Los grafos enfatizan las relaciones complejas de conexión entre elementos de datos. Los árboles son un caso simple de grafos. 
Los grafos permiten estudiar problemas como: menor distancia o costo entre puntos; búsquedas a través de enlaces en la web; redes eléctricas, cableado de impresos; itineración de tareas, siguiendo trayectorias; sintonizar o relacionar postulantes con ofertas, partes con proveedores; conectividad entre pares de sitios en la red; especificación de aplicaciones mostrando las relaciones entre subsistemas; etc. 
10.1. Definiciones. 
Quedan definidos por un conjunto de vértices V, más un conjunto de elementos E que conectan pares de vértices distintos. El grafo se describe por: G(V, E). 
Figura 10.1. Elemento, vértice, incidencia. 
Grado de incidencia, del vértice, es el número de elementos que son incidentes en ese vértice. 
En grafos simples se aceptan sólo un enlace entre un par de vértices diferentes. Es decir, no se aceptan lazos (elementos adyacentes en el mismo vértice), ni elementos en paralelo (dos elementos que son adyacentes con un mismo par de vértices. 
Trayectoria: Secuencia de elementos entre vértices adyacentes. 
Trayectoria simple: los vértices y los elementos están presentes sólo una vez. 
Circuito o ciclo: Trayectoria simple, salvo que el vértice inicial y final son uno solo. 
Largo de la trayectoria es el número de elementos que la componen. 
Grafo conectado: Si existe trayectoria entre cualquier par de vértices. 
Árbol de un grafo: Subgrafo conectado sin circuitos. 
vértice elemento Vértices adyacentes Elemento incidente en vértice
2 Estructuras de Datos y Algoritmos 
Profesor Leopoldo Silva Bijit 20-01-2010 
Densidad es el promedio de los grados de incidencia de los vértices. Si sumamos los grados de 
incidencia se tendrá el valor 2E (se cuenta dos veces cada elemento). Entonces la densidad 
resulta: 2E/V. 
Se dice que un grafo es denso si su densidad en proporcional a V. Esto implica que el número 
de elementos E será proporcional a V2. En caso contrario es un grafo liviano (sparse). 
El concepto de densidad tiene relación con el tipo de algoritmo y la estructura de datos que se 
empleará para representar el grafo. Complejidades típicas son V2 y E*logE, la primera es más 
adecuada a grafos densos; y la segunda a grafos livianos. 
Grafos dirigidos: Los elementos tienen asociada una dirección. 
Figura 10.2. Elementos orientados. 
Grafos ponderados: Los elementos ( w(E) ) o los vértices tienen asociado una distancia o 
costo. 
Árbol de cobertura (Spanning tree) de un grafo conectado, con pesos y no dirigido, es un árbol 
cuyo peso se calcula según: 
E T 
w(T) w(E) 
De los innumerables problemas asociados a grafos se estudiarán dos. 
El mínimo árbol de cobertura, en grafos con peso pero no dirigidos. 
La ruta mínima: en grafos con peso y dirigidos. 
10.2. Representaciones. 
Para grafos densos se prefiere la matriz de adyacencias, para grafos livianos suele emplearse 
una lista de adyacencias. 
10.2.1. Matriz de adyacencia de los elementos en los vértices. 
Se emplea una matriz cuadrada (V-1, V-1) donde V es el número de vértices. 
a 
Elemento= (a, b) 
b a 
Elemento= (b, a) 
b
Grafos 3 
Profesor Leopoldo Silva Bijit 20-01-2010 
m[0][0] 
m[0][3] 
m[1][0] 
m[1][3] 
a 0 1 .. w .. V-1 
0 
1 
.. 
v 1 
.. 
V-1 
Figura 10.3. Matriz de adyacencias. 
Se coloca un 1 en (v, w) si existe elemento de v hacia w; 0 en caso contrario. 
La matriz es simétrica si el grafo no es dirigido. Si no se aceptan lazos, la diagonal está formada 
por ceros. 
10.2.2. Matrices estáticas en C. 
La notación: a[v][w] recuerda que la matriz a se visualiza como un arreglo de v renglones, 
donde cada renglón está formado por w columnas. 
Ejemplo: 
La matriz m se define según: 
int m[2][4]; 
0 
2 x 4 
Figura 10.4. Matriz de 2 renglones por 4 columnas. 
Se almacenan por renglones. La siguiente figura ilustra el almacenamiento de los elementos del 
arreglo, con direcciones crecientes hacia abajo: 
Figura 10.5. Forma de almacenamiento de matrices. 
Índice más derechista varía más rápido, si se accesa según el orden de almacenamiento. 
Se inicializan mediante listas: 
int m[2][4]={{0,1,2,3},{4,5,6,7}};
4 Estructuras de Datos y Algoritmos 
Profesor Leopoldo Silva Bijit 20-01-2010 
Matrices como argumentos. 
Si se pasa una matriz a una función, la declaración del argumento debe incluir la dimensión de la columna. La dimensión del renglón es irrelevante, ya que se pasa un puntero. 
f(int m[ ] [4]) ó f(int (*m)[4]) 
10.2.3. Matrices dinámicas en C. 
Ver Sedgewick, cap. 17. 
Las siguientes declaraciones describen un grafo a través de una matriz de adyacencias. 
Declaración de estructuras de datos para un grafo. 
struct graph { 
int V; //Número de vértices 
int E; //Número de elementos 
int **adj; // Matriz de adyacencias 
}; 
typedef struct graph *Graph; 
Definición de variables. 
Las siguientes definiciones crean el espacio. Se define un grafo G, cuya matriz de adyacencias se define según un arreglo de r punteros para almacenar las direcciones de los arreglos de c elementos: 
Graph G = malloc(sizeof *G); //crea la cabecera del grafo. 
int **t = malloc(r * sizeof(int *)); //crea arreglo de r renglones de punteros 
G->adj = t; //Pega el arreglo de punteros 
//crea y pega los renglones de c columnas: 
for (i = 0; i < r; i++) t[i] = malloc(c * sizeof(int)); 
El siguiente diagrama ilustra las variables. Se ha omitido la casilla asociada a t.
Grafos 5 
Profesor Leopoldo Silva Bijit 20-01-2010 
Figura 10.6. Estructura para matriz de adyacencias. 
También se puede definir una matriz estática (sin llamar a malloc()) : como un arreglo de r punteros que apuntan a arreglos con la información de las columnas. 
10.2.4. Funciones para grafos descritos por su matriz de adyacencias. 
La función creación de un grafo vacío de V vértices: 
10.2.4.1. Creación. 
Graph GRAPHinit(int V) 
{ Graph G = malloc(sizeof *G); //crea cabecera del grafo 
G->V = V; G->E = 0; //Con V vértices y 0 elementos 
G->adj = MATRIXint(V, V, 0); //Lo inicia con ceros. 
return G; 
} 
Donde MATRIXint crea la matriz de incidencias de r renglones, c columnas y la inicializa con val. 
//Asignación dinámica de arreglo bidimensional 
int **MATRIXint(int r, int c, int val) 
{ int i, j; 
int **t = malloc(r * sizeof(int *)); 
for (i = 0; i < r; i++) 
t[i] = malloc(c * sizeof(int)); // t[i] equivale a *(t+i) 
for (i = 0; i < r; i++) 
for (j = 0; j < c; j++) 
t[i][j] = val; //equivale a **(t+i+j) = val; 
return t; 
j i t t[i] ó *(t+i) t[i][j] ó **(t+i+j) G V E adj
6 Estructuras de Datos y Algoritmos 
Profesor Leopoldo Silva Bijit 20-01-2010 
} 
De complejidad: O(r + r*c) 
La siguiente función, libera el espacio asociado al grafo, cuidando de borrar en orden inverso al solicitado, de tal modo de no perder las referencias. 
10.2.4.2. Liberación del espacio. 
void BorreGrafo(Graph G) 
{ int i; 
int **t = G->adj; 
for (i = 0; i < G->V; i++) free(t[i]); //primero borra los renglones 
free(t); //luego el arreglo de punteros a los renglones 
free(G); //finalmente la cabecera 
} 
Si se tiene la definición: 
Graph Grafo; 
Puede crearse la matriz de adyacencias del grafo mediante: 
#define VERTICES 5 
Grafo = GRAPHinit(VERTICES); 
10.2.4.3. Definición de un elemento. 
Un elemento puede describirse por: 
typedef struct 
{ 
int v; //vértice inicial. Desde. 
int w; //vértice final. Hasta. 
} Edge; 
Con el siguiente constructor: 
Edge EDGE(int v, int w) 
{ Edge t; 
t.v=v; t.w=w; 
return (t); 
} 
La siguiente definición crea un elemento, y EDGE lo inicializa. 
Edge elemento; 
elemento = EDGE(1,2); 
10.2.4.4. Inserción de elemento en un grafo. 
La siguiente función inserta un elemento e en un grafo G.
Grafos 7 
Profesor Leopoldo Silva Bijit 20-01-2010 
void GRAPHinsertE(Graph G, Edge e) 
{ 
if (G->adj[e.v][e.w] == 0) G->E++; //aumenta el número de elementos del grafo 
G->adj[e.v][e.w] = 1; 
G->adj[e.w][e.v] = 1; //si el grafo no es dirigido. 
} 
10.2.4.5. Eliminación de elemento. 
La función siguiente remueve el elemento e del grafo G: 
void GRAPHremoveE(Graph G, Edge e) 
{ 
if (G->adj[e.v][e.w] == 1) G->E--; //Disminuye el contador de elementos. 
G->adj[e.v][e.w] = 0; 
G->adj[e.w][e.v] = 0; 
} 
La acción siguiente, inserta el elemento en el Grafo: 
GRAPHinsertE(Grafo, elemento); 
10.2.4.6. Creación de los elementos. 
La siguiente definición crea el conjunto de elementos de un grafo, como un arreglo denominado Elementos. Esta es otra forma de definir un grafo. Requiere 2E datos para ser definida, en lugar de V2 que necesita la matriz de incidencia. 
#define ELEMENTOS 6 
Edge Elementos[ELEMENTOS]={{1,2},{1,4},{2,3},{3,4},{4,0},{3,0} }; 
elemento 
v 
w 
0 
1 
2 
1 
1 
4 
2 
2 
3 
3 
3 
4 
4 
4 
0 
5 
3 
0 
Figura 10.7. Grafo descrito por sus elementos. 
Esta descripción es única, en el sentido que asocia nombres de elementos con nombres de vértices. 
Entonces la creación de la matriz de incidencia de un grafo de cinco vértices, a partir del arreglo de elementos, definido por seis elementos, puede realizarse, según:
8 Estructuras de Datos y Algoritmos 
Profesor Leopoldo Silva Bijit 20-01-2010 
for(i=0; i<ELEMENTOS; i++) GRAPHinsertE(Grafo, Elementos[i] ); 
que tiene complejidad O(E). 
Mostrando que la matriz de incidencia puede obtenerse del arreglo de los elementos, lo cual indica que son representaciones equivalentes. 
El grafo resultante, puede visualizarse, según: 
Figura 10.8. Grafo para elementos de la figura 10.7. 
10.2.4.7. Despliegue de un grafo. Lista de vértices. 
Para visualizar el grafo, puede generarse una lista de los vértices: con los vértices que tienen conectados elementos a ese vértice. 
//muestra la matriz como listas de vértices que tienen conexión con cada vértice. 
void GRAPHshowL(Graph G) 
{ int i, j; 
printf("%d vertices, %d edgesn", G->V, G->E); //número total de elementos y vértices. 
for (i = 0; i < G->V; i++) 
{ printf("%2d:", i); 
for (j = 0; j < G->V; j++) 
if (G->adj[i][j] == 1) printf(" %2d", j); 
putchar(‘n’); 
} 
} 
De complejidad O(V2). 
El cual entregaría el siguiente listado. 
5 vertices, 6 edges 
0: 3 4 
1: 2 4 
2: 1 3 
3: 0 2 4 
4: 0 1 3 
0 3 4 1 2 0 1 2 5 3 4
Grafos 9 
Profesor Leopoldo Silva Bijit 20-01-2010 
10.2.4.8. Despliegue de la matriz de incidencias. 
//Muestra Matriz de incidencia. 
void GRAPHshowM(Graph G) 
{ int i, j; 
printf("%d vertices, %d edgesn", G->V, G->E); 
printf(" "); 
for (j = 0; j < G->V; j++) printf(" %2d", j); 
putchar(‘n’); 
for (i = 0; i < G->V; i++) 
{ 
printf("%2d:", i); 
for (j = 0; j < G->V; j++) 
if (G->adj[i][j] == 1) printf(" %2d", 1); else printf(" %2d", 0); 
putchar(‘n’); 
} 
} 
Que genera, para el ejemplo: 
5 vertices, 6 edges 
0 1 2 3 4 
0: 0 0 0 1 1 
1: 0 0 1 0 1 
2: 0 1 0 1 0 
3: 1 0 1 0 1 
4: 1 1 0 1 0 
10.2.4.9. Generación de los elementos a partir de la matriz de incidencias. 
Si el grafo ya está construido, la generación de los elementos, a partir del grafo, se logra con la función: 
int GRAPHedges(Edge a[], Graph G) 
{ int v, w, E = 0; //numera los elementos desde el cero. 
for (v = 0; v < G->V; v++) //Para todos los renglones 
for (w = v+1; w < G->V; w++) //revisa por columnas 
if (G->adj[v][w] == 1) 
a[E++] = EDGE(v, w); //escribe por referencia 
return E; //retorna el número de elementos. 
} 
Se advierte que debido a los dos for anidados es O( (V2-V)/2 ), ya que revisa sobre la diagonal. 
Encontrar los elementos a partir del grafo se realiza con: 
GRAPHedges(Elementos, Grafo); //Llena el arreglo a partir del Grafo. 
Nótese que al recorrer la submatriz sobre la diagonal, por renglones, va reasignando, a partir de cero, los nombres de los elementos, y sus correspondientes vértices.
10 Estructuras de Datos y Algoritmos 
Profesor Leopoldo Silva Bijit 20-01-2010 
elemento v w 
0 0 3 
1 0 4 
2 1 2 
3 1 4 
4 2 3 
5 3 4 
Figura 10.9. Generación de los elementos a partir de matriz de adyacencias. 
Debido a que la matriz de adyacencias no almacena información sobre el nombre de los 
elementos, el arreglo de elementos toma los nombres dados por el recorrido. 
10.3. Trayectorias en grafos. 
Un primer problema es determinar si existe una trayectoria entre dos vértices. 
Si se define un arreglo en que se marque si los nodos han sido o no visitados, puede plantearse 
el siguiente esquema recursivo: 
Si el vértice inicial y el final son iguales, hay trayectoria (fin de recursión). 
Marcar el vértice inicial como visitado. 
Revisar todos los vértices, conectados al inicial: 
Si uno no ha sido revisado: ver si hay trayectoria entre ese y el final. 
Si revisados todos no hay trayectoria, entonces no existe la trayectoria buscada. 
int pathR(Graph G, int v, int w) 
{ int t; 
if (v == w) return 1; //Existe trayecto. Nodo inicial y final son iguales. 
visited[v] = 1; 
for (t = 0; t < G->V; t++) 
if (G->adj[v][t] == 1) //Si v está conectado con t 
if (visited[t] == 0) //y t no ha sido visitado 
{ printf("%d-%d ", v, t); //Debug: Muestra elemento de trayectoria en prueba. 
if (pathR(G, t, w)) return 1; 
} 
return 0; 
} 
0 3 
4 
1 2 2 
3 
4 
0 
5 
1 
0 1 2 3 4 
0 0 0 0 1 1 
1 0 0 1 0 1 
2 0 1 0 1 0 
3 1 0 1 0 1 
4 1 1 0 1 0
Grafos 11 
Profesor Leopoldo Silva Bijit 20-01-2010 
int GRAPHpath(Graph G, int v, int w) 
{ int t; 
for (t = 0; t < G->V; t++) visited[t] = 0; 
return pathR(G, v, w); //Inicia búsqueda recursiva. 
} 
Un ejemplo de invocación: 
if( GRAPHpath(Grafo, 1, 3)) 
printf("existe trayectoria, entre 1 y 3n"); 
else printf("no existe trayectoria.n"); 
Existen dos formas básicas de exploración de un grafo: La búsqueda primero en profundidad 
(Depth-first search) y la búsqueda primero en extensión (Breadth –first search). 
10.3.1. Búsqueda primero en profundidad. 
10.3.1.1. Definición de búsqueda primero en profundidad. 
Se recorre el grafo, siempre hacia adelante hasta que se llega al final o hasta una trayectoria que 
ya se ha recorrido; luego se devuelve y busca trayectorias no exploradas. El objetivo es buscar 
un árbol. 
Se visita y marca un vértice como visitado, luego se visitan (recursivamente) todos los vértices 
adyacentes al recién marcado que no estén visitados. Esto va formando un árbol, con el orden en 
que se van visitando los vértices. 
Para el grafo a la izquierda de la Figura 10.10.: 
Si el vértice inicial es el 0 (se pasa el elemento (0,0), se lo visita y marca con la cuenta 0, y se 
generan llamados con los elementos (0,3) y (0,4), en ese orden. 
Figura 10.10. Orden de visitas, generación de llamados. 
El llamado con el elemento (0, 3) marca el 3 con la cuenta 1, y se generan los llamados: con (3, 
2), (3,4). 
El llamado con el elemento (3, 2) marca el 2 con la cuenta 2, y se genera el llamado: (2,1). 
El llamado con el elemento (2, 1) marca el 1 con la cuenta 3, y se genera el llamado: (1,4). 
El llamado con el elemento (1, 4) marca el 4 con la cuenta 4 y no genera nuevos llamados. 
Resultando el árbol T(1, 2, 3, 4) ilustrado en la figura a la derecha. 
0 3 
4 
1 2 
0 3 
4 
1 3 2 
4 
2 
1
12 Estructuras de Datos y Algoritmos 
Profesor Leopoldo Silva Bijit 20-01-2010 
10.3.1.2. Diseño de la operación. 
La función recursiva: 
void dfsR(Graph G, Edge e) 
{ int t, w = e.w; 
pre[w] = cnt++; //Marca con contador creciente 
for (t = 0; t < G->V; t++) 
{ 
if (G->adj[w][t] != 0) //Si hay conexión entre w y t 
if (pre[t] == -1) dfsR(G, EDGE(w, t)); //Y si t no está visitado sigue buscando. 
} 
} 
La función que efectúa la búsqueda en profundidad: 
void GRAPHsearchDFS(Graph G) //depth-first-search 
{ int v; 
cnt = 0; //es variable global 
for (v = 0; v < G->V; v++) pre[v] = -1; //Marca todos como no visitados 
for (v = 0; v < G->V; v++) //Revisa todos los vértices. 
if (pre[v] == -1) 
dfsR(G, EDGE(v, v)); //Se invoca varias veces si el grafo es no conectado. 
} 
Se visitan todos los elementos y todos los vértices conectados al vértice de partida, no importando el orden en que revisa los elementos incidentes en ese vértice. La estrategia recursiva implica un orden: el último que entró es el primero que salió (LIFO), de los elementos posibles se elige el más recientemente encontrado. 
10.3.1.3. Modificación para entender la operación. 
Se agrega la variable estática indente, para ir mostrando los elementos que son sometidos a revisión. Cada vez que se genera un llamado se produce un mayor nivel de indentación; el cual es repuesto al salir del llamado recursivo. 
Se agrega un asterisco para mostrar los elementos que generan llamados recursivos. 
static int indente=0; 
void dfsR(Graph G, Edge e) 
{ int t,j, w = e.w; 
pre[w] = cnt++; 
for (t = 0; t < G->V; t++) 
{ 
if (G->adj[w][t] != 0) 
{ for (j = 0; j < indente; j++) printf(" "); 
printf("%d-%d n", w, t); 
if (pre[t] == -1) 
{ indente++; 
putchar('*'); dfsR(G, EDGE(w, t));
Grafos 13 
Profesor Leopoldo Silva Bijit 20-01-2010 
indente--; 
} 
else putchar(' '); 
} 
} 
} 
Para el grafo del ejemplo de la figura 10.10., se produce el listado: 
0-3 
* 3-0 
3-2 
* 2-1 
* 1-2 
1-4 
* 4-0 
4-1 
4-3 
2-3 
3-4 
0-4 
Este algoritmo puede emplearse: para detectar circuitos, para saber si existe una trayectoria que conecte a dos vértices dados, para determinar si es un grafo conectado, para encontrar un árbol. Ver propiedades de los árboles dfs en Sedgewick 18.4. 
10.3.1.4. Arreglo de padres. 
La siguiente modificación permite almacenar en el arreglo st el padre del vértice w. Esa información es útil para aplicaciones de este algoritmo. Y es una forma de describir el árbol. 
static int st[VERTICES]; 
void dfsR(Graph G, Edge e) 
{ int t, j, w = e.w; 
pre[w] = cnt++; //Marca con contador creciente 
st[e.w] = e.v; //Se guarda el padre de w. 
for (t = 0; t < G->V; t++) 
{ 
if (G->adj[w][t] != 0) //Si hay conexión entre w y t 
if (pre[t] == -1) dfsR(G, EDGE(w, t)); //Y t no está visitado busca t. 
} 
} 
Para el ejemplo, quedarían almacenados en st: 0, 2, 3, 0, 1. Y en pre: 0, 3, 2, 1, 4. 
El árbol que genera este algoritmo, se produce revisando los elementos en el siguiente orden:
14 Estructuras de Datos y Algoritmos 
Profesor Leopoldo Silva Bijit 20-01-2010 
0-3 , *3-0 , 3-2, *2-1, *1-2, 1-4, *4-0, 4-1, 4-3, 2-3, 3-4, 0-4. Los que generan llamados 
recursivos, se preceden con un asterisco. La ilustración muestra que se avanza primero en 
profundidad. 
0 
3 4 
0 2 4 
1 3 
2 4 
0 1 3 
Figura 10.11. Visualización de los llamados, en profundidad. 
10.3.1.5. Uso de stack en búsqueda en profundidad. 
Se emplea un stack para guardar los vértices. 
//árbol en profundidad. No recursivo 
void dfs(Graph G, Edge e) 
{ int t, v, w, f; 
StackInit(G->V*2); 
StackPush(e.v); StackPush(e.w); //empuja los vértices del elemento 
while(!StackEmpty()) 
{ w=StackPop(); 
v=StackPop(); 
StackPush(v); f=1; //guarda ruta en el árbol 
pre[w] = cnt++; 
st[w] = v; // se guarda el vértice padre de w. 
while(f) 
{ 
for (t = 0; t < G->V; t++) 
{ if (G->adj[w][t] != 0) 
if (pre[t] == -1) 
{ StackPush(w); 
StackPush(t); 
printf("*%d-%d n", w, t); f=0; 
break; 
} 
}
Grafos 15 
Profesor Leopoldo Silva Bijit 20-01-2010 
if (f) //llegó al final de un recorrido en el árbol. 
{if (!StackEmpty()) w=StackPop(); //extrae vértice anterior del árbol 
else f=0; 
} 
} 
} 
Stackdestroy(); 
} 
void GRAPHsearchDFSnr(Graph G) 
{ int v; 
cnt = 0; 
for (v = 0; v < G->V; v++) {pre[v] = -1; st[v] = -1;} 
for (v = 0; v < G->V; v++) 
if (pre[v] == -1) 
dfs(G, EDGE(v, v)); 
} 
10.3.2. Búsqueda primero en extensión. 
10.3.2.1. Definición de la búsqueda primero en extensión. 
La búsqueda en extensión tiene por objetivo encontrar la ruta más corta entre dos vértices dados. También se genera un árbol. 
Se parte de un nodo, y se busca el siguiente nodo en todas las rutas de largo uno que existan; luego se buscan todas las rutas de largo dos, y así sucesivamente. 
Explorar los vértices, de acuerdo a su distancia al de partida, implica que de los elementos que son posibles, se escoge uno y los otros se salvan para ser posteriormente explorados. Este orden es: el primero que se encuentra, es el primero en ser procesado (FIFO). 
Para visitar un vértice: se buscan los elementos que son incidentes con ese vértice, y los elementos que tienen el otro vértice no visitado, se encolan. 
Para el siguiente grafo: 
Figura 10.12. Ejemplo para búsqueda primero en extensión. 
0 3 4 1 2
16 Estructuras de Datos y Algoritmos 
Profesor Leopoldo Silva Bijit 20-01-2010 
A partir del nodo inicial 0, se marca el 0, y se exploran los elementos: 0-3 y 0-4. Se encolan el 0-3 y el 0-4. Ya que el 3 y 4 no han sido visitados aún. 
Se desencola el 0-3, se marca el 3, y se revisan el 3-0, el 3-2 y el 3-4. Se encolan el 3-2 y el 3-4. Ya que el 2 y el 4 no han sido visitados aún. 
Se desencola el 0-4, se marca el 4, y se revisan el 4-0, 4-1 y 4-3. Se encola el 4-1. 
Se desencola el 3-2, se marca el 2, y se revisan el 2-1, 2-3. Se encola el 2-1. 
Se desencola el 3-4 sin procesar. 
Se desencola el 4-1, se marca el 1, y se revisan el 1-2 y 1-4. 
Se desencola el 2-1 sin procesar. 
Del árbol formado, se visualiza, que el vértice 0 está a distancia uno de los vértices 3 y 4, y que está a distancia 2, de los vértices 1 y 2. 
Los vértices se marcan en el siguiente orden: 0, 3, 4, 2, 1. 
Figura 10.13. Orden de visitas, generación de llamados. En extensión. 
La figura10.13. ilustra que se busca por largos de trayectos. 
10.3.2.2. Diseño de la operación. 
El código de la función que encola los elementos. En la cola se almacenan elementos. 
void bfs(Graph G, Edge e) //breadth-first-search 
{ int v; 
QUEUEput(e); 
while (!QUEUEempty()) 
if (pre[(e = QUEUEget()).w] == -1) 
{ 
pre[e.w] = cnt++; //en pre queda el orden en que se escogen los vértices 
st[e.w] = e.v; //en st queda el padre. 
for (v = 0; v < G->V; v++) 
if (G->adj[e.w][v] == 1) 
if (pre[v] == -1) QUEUEput(EDGE(e.w, v)); 
} 
} 
La función que genera el árbol BFS. 
void GRAPHsearchBFS(Graph G) 
{ int v; 
0 3 4 0 2 4 1 3 2 4 0 1 3
Grafos 17 
Profesor Leopoldo Silva Bijit 20-01-2010 
cnt = 0; 
QUEUEinit(ELEMENTOS); 
for (v = 0; v < G->V; v++) {pre[v] = -1; st[v]=-1;} //Inicio de estructuras 
for (v = 0; v < G->V; v++) // Para todos los vértices 
if (pre[v] == -1) 
bfs(G, EDGE(v, v)); //Se invoca una vez, si el grafo es conectado. 
QUEUEdestroy(); 
} 
10.4. Árboles con peso. 
Si los elementos tienen asociado un peso se desea encontrar un árbol tal que la suma de los pesos de sus ramas sea mínimo. 
Previo a resolver este problema es necesario modificar las estructuras de datos para incorporar el peso del elemento. Puede escogerse un número real entre 0. y menor que 1. Esto puede lograrse dividiendo los pesos reales por el peso del mayor levemente incrementado. 
Modificación de las funciones para tratar grafos con pesos. 
Se elige: 
typedef struct 
{ 
int v; //vértice inicial 
int w; //vértice final 
float wt; //peso. Puede ser un double 
} Edge; 
El constructor queda ahora: 
Edge EDGE(int v, int w, float wt) 
{ 
Edge t; 
t.v=v; t.w=w; t.wt=wt; 
return (t); 
} 
Para un grafo se definen: 
struct graph { 
int V; //Número de vértices 
int E; //Número de elementos 
float **adj; // Matriz de adyacencias 
}; 
typedef struct graph *Graph; 
Para marcar la no existencia de adyacencias, se define: 
#define maxWT 1. //Todo elemento tiene menor peso que maxWT.
18 Estructuras de Datos y Algoritmos 
Profesor Leopoldo Silva Bijit 20-01-2010 
La inicialización de un grafo vacío, se logra con: 
Graph GRAPHinit(int V) 
{ Graph G = malloc(sizeof *G); //crea cabecera del grafo 
G->V = V; G->E = 0; 
G->adj = MATRIXfloat(V, V, maxWT); 
return G; 
} 
Donde la función que localiza espacio dinámico para la matriz de adyacencias es: 
float **MATRIXfloat(int r, int c, float wt) 
{ int i, j; 
float **t = malloc(r * sizeof(float *)); 
for (i = 0; i < r; i++) 
t[i] = malloc(c * sizeof(float)); 
for (i = 0; i < r; i++) 
for (j = 0; j < c; j++) 
t[i][j] = wt; //**(t+i+j) = wt; 
return t; 
} 
El resto de las funciones se modifican para tratar grafos ponderados: 
void BorreGrafo(Graph G) //Libera el espacio adquirido por malloc. 
{ int i; 
float **t = G->adj; 
for (i = 0; i < G->V; i++) free(t[i]); 
free(t); 
free(G); 
} 
void GRAPHinsertE(Graph G, Edge e) //Inserta elemento 
{ 
if (G->adj[e.v][e.w] == maxWT) G->E++; 
G->adj[e.v][e.w] = e.wt; 
G->adj[e.w][e.v] = e.wt; //suprimir para grafos dirigidos 
} 
void GRAPHremoveE(Graph G, Edge e) //Remueve elemento 
{ 
if (G->adj[e.v][e.w] != maxWT) G->E--; 
G->adj[e.v][e.w] = maxWT; 
G->adj[e.w][e.v] = maxWT; //suprimir para grafos dirigidos 
} 
int GRAPHedges(Edge a[], Graph G) //Forma arreglo de elementos a partir del grafo 
{ int v, w, E = 0;
Grafos 19 
Profesor Leopoldo Silva Bijit 20-01-2010 
for (v = 0; v < G->V; v++) 
for (w = v+1; w < G->V; w++) 
if (G->adj[v][w] != maxWT) a[E++] = EDGE(v, w, G->adj[v][w]); 
return E; 
} 
//muestra la matriz mediante listas de vértices conectados a cada vértice 
void GRAPHshowL(Graph G) 
{ int i, j; 
printf("%d vertices, %d edgesn", G->V, G->E); 
for (i = 0; i < G->V; i++) 
{ 
printf("%d: ", i); 
for (j = 0; j < G->V; j++) 
if (G->adj[i][j] != maxWT) printf(" %0.2f", G->adj[i][j]); 
putchar('n'); 
} 
} 
//Muestra Matriz de adyacencias. 
void GRAPHshowM(Graph G) 
{ int i, j; 
printf("%d vertices, %d edgesn", G->V, G->E); 
printf(" "); 
for (j = 0; j < G->V; j++) printf(" %4d ", j); 
printf("n"); 
for (i = 0; i < G->V; i++) 
{ 
printf("%2d:", i); 
for (j = 0; j < G->V; j++) 
if (G->adj[i][j] != maxWT) printf(" %0.2f", G->adj[i][j]); else printf(" * "); 
putchar('n'); 
} 
} 
Se limita a dos el número de cifras significativas. 
Las siguientes líneas describen un grafo a partir de sus elementos. 
#define VERTICES 8 
#define ELEMENTOS 12 
//Variables 
Graph Grafo; 
//Edge Elementos[ELEMENTOS]={{0,2,.29},{4,3,.34},{5,3,.18},{7,4,.46},{7,0,.31}, 
{7,6,.25},{7,1,.21},{0,6,.51},{6,4,.52},{4,5,.40}, 
{5,0,.59},{0,1,.32} };
20 Estructuras de Datos y Algoritmos 
Profesor Leopoldo Silva Bijit 20-01-2010 
La descripción del grafo, descrito por sus elementos, según la lista de vértices adyacentes resulta: 
8 vértices, 12 elementos 
0: 0.32 0.29 0.59 0.51 0.31 
1: 0.32 0.21 
2: 0.29 
3: 0.34 0.18 
4: 0.34 0.40 0.52 0.46 
5: 0.59 0.18 0.40 
6: 0.51 0.52 0.25 
7: 0.31 0.21 0.46 0.25 
La matriz de adyacencias con los pesos, resulta: 
0 1 2 3 4 5 6 7 
0: * 0.32 0.29 * * 0.59 0.51 0.31 
1: 0.32 * * * * * * 0.21 
2: 0.29 * * * * * * * 
3: * * * * 0.34 0.18 * * 
4: * * * 0.34 * 0.40 0.52 0.46 
5: 0.59 * * 0.18 0.40 * * * 
6: 0.51 * * * 0.52 * * 0.25 
7: 0.31 0.21 * * 0.46 * 0.25 * 
El siguiente ejemplo muestra las modificaciones a la generación de un árbol, empleando búsqueda primero en profundidad. Se agrega el vector wt con los pesos. 
static int cnt; 
static int pre[VERTICES]; 
static int st[VERTICES]; 
static float wt[VERTICES]; 
void dfsR(Graph G, Edge e) 
{ int t; 
pre[e.w] = cnt++; 
st[e.w] = e.v; //se guarda el vértice v padre de w. 
wt[e.w]=G->adj[e.w][e.v]; //se guarda el peso del elemento w-v 
for (t = 0; t < G->V; t++) 
{ 
if (G->adj[e.w][t] != maxWT) 
if (pre[t] == -1) 
{dfsR(G, EDGE(e.w, t, maxWT)); /*printf("%d-%d n", e.w, t);*/} 
} 
}
Grafos 21 
Profesor Leopoldo Silva Bijit 20-01-2010 
void GRAPHsearchDFS(Graph G) 
{ int v; 
cnt = 0; 
for (v = 0; v < G->V; v++) {pre[v] = -1; st[v] = -1;} //Inicialización 
for (v = 0; v < G->V; v++) 
if (pre[v] == -1) 
dfsR(G, EDGE(v, v, maxWT)); 
} 
La cual entrega en st la descripción del árbol a partir de un arreglo de padres de los vértices. 
0 1 2 3 4 5 6 7 Vértices. 
0 0 0 4 7 3 4 1 Padre del vértice. 
1.0 0.32 0.29 0.34 0.46 0.18 0.52 0.21 Peso del elemento entre vértice y su Padre. 
La raíz del árbol (vértice 0) tiene un lazo de peso infinito (valor 1.0) consigo misma. 
Este árbol cubre todos los nodos, pero no es un árbol de cobertura mínima. 
10.5. Mínimo árbol de cobertura. 
Para un grafo dado existe un muy elevado número de árboles. No es fácil encontrar el árbol de cobertura mínima. 
Algunos conceptos para definir el árbol de cobertura mínima: 
Un elemento del árbol se denomina rama. 
Si se agrega un elemento a un árbol se crea un único circuito, el elemento agregado suele denominarse cuerda. Un circuito fundamental está formado por una cuerda y el resto de los elementos deben ser ramas. 
Un conjunto de corte son los elementos que unen dos conjuntos disjuntos de vértices. 
Uno de los elementos del conjunto de corte debe ser rama. 
Si los elementos tienen pesos diferentes, la rama del conjunto de corte debe tener peso mínimo. Si existen múltiples elementos mínimos en el corte, al menos uno de ellos debe estar presente. 
Entonces: Cada rama de un mínimo árbol de cobertura es el elemento mínimo de un conjunto de corte. 
Así también las cuerdas deben tener los máximos pesos de los circuitos que formen con el mínimo árbol de cobertura. 
La formación de un MST (minimum spanning tree) consiste en aplicar las reglas anteriores, para rechazar elementos que sean cuerdas, con peso máximo; y aceptar ramas, con pesos mínimos. 
El algoritmo de Prim, elige un vértice cualquiera como inicial.
22 Estructuras de Datos y Algoritmos 
Profesor Leopoldo Silva Bijit 20-01-2010 
Luego repite para los (V-1) vértices restantes: 
Agregar elemento de peso mínimo que conecte los vértices del MST, con los vértices que aún no pertenecen al MST. 
El algoritmo de Kruskal procesa los elementos ordenados por sus pesos. Se va agregando al MST un elemento que no forme circuitos con los previamente agregados, y se detiene si se han agregado (V-1) elementos. Esto porque en grafos conectados el árbol tiene (V-1) ramas. 
10.5.1. Algoritmo de Prim. 
Si se tienen vértices en el MST, se busca un vértice w que aún no está en el árbol, tal que esté a menor distancia de los vértices del MST. Para esto es preciso registrar las menores distancias, de cada nodo no perteneciente al MST, a los nodos pertenecientes al MST; y elegir la menor. 
Figura 10.14. Conjuntos en el algoritmo de Prim. 
Para diseñar el algoritmo se emplean tres arreglos: 
static int padre[VERTICES]; // padre del vértice 
static int DistaMenosDe[VERTICES]; //vértice que dista menos del vértice del árbol. 
static float wt[VERTICES+1]; //distancia menor. Con espacio para centinela. 
El árbol se describe por el arreglo padre[v], donde se almacena el padre del vértice v. Se inicia con valores iguales a menos uno, para indicar que ningún vértice forma parte del árbol. 
Se agrega un arreglo DistaMenosDe[w], en el cual, durante el procesamiento, se dejará el vértice más cercano a w del árbol. Se inicia con DistaMenosDe[i] = i, indicando que esta información no se conoce. 
Antes se empleó un arreglo wt[w] para almacenar los pesos de los elementos, ahora se usa para almacenar la mínima distancia al árbol, si el vértice w aún no pertenece al árbol; y la distancia al padre, si el vértice w ya pertenece al MST. Al inicio se lo llena con maxWT, para indicar que esta información sobre pesos mínimos aún no se conoce. 
En la variable local min se almacena el vértice, que aún no pertenece al MST, y el cual debe cumplir la propiedad de tener distancia mínima con los vértices que ya están en el MST. Para asociarle un peso, se agrega una entrada al arreglo wt, con valor maxWT, y se lo inicia con valor V (vértice que no existe en el grafo). De esta forma wt[min] tendrá un espacio y valor definido. 
MST w
Grafos 23 
Profesor Leopoldo Silva Bijit 20-01-2010 
#define NoVisitado -1 
#define SinPadre -1 
#define Peso G->adj[v][w] 
void mstPrim(Graph G, int raiz) 
{ //Inicio del espacio de variables 
int v, w, min=raiz, cnt; 
for (v = 0; v < G->V; v++) 
{ padre[v] = SinPadre; DistaMenosDe[v] = v; wt[v] = maxWT; } 
wt[G->V] = maxWT; //centinela. Arreglo requiere una posición adicional 
wt[raiz]=0.; //raíz a distancia cero de sí misma. 
for (cnt=0; cnt< G->V; cnt++) //agrega todos los vertices O(V2) 
{ 
v = min; padre[v] = DistaMenosDe[v]; //agrega vértice v al MST 
//printf(" %d n", v); //orden en que son agregados los vértices. 
//Selecciona vértice min con distancia mínima a los vértices del mst 
for (w = 0, min = G->V; w < G->V; w++) 
//Al inicio min es un vértice que no existe. 
if (padre[w] == NoVisitado) //si w no está aún en el MST 
{ 
if (Peso < wt[w]) 
{ wt[w] = Peso; DistaMenosDe[w] = v; } //salva distancia menor y el vértice. 
if (wt[w] < wt[min]) min = w; //selecciona nuevo vértice a distancia mínima 
} 
//Al salir del for interno se tiene el nuevo vértice que se agrega al MST 
} 
} 
Para los datos anteriores, luego de ejecutado el Algoritmo de Prim, con raíz en vértice 0, se genera: 
0 1 2 3 4 5 6 7 Vértices. 
0 7 0 4 7 3 7 0 Padre del vértice. 
0 0.21 0.29 0.34 0.46 0.18 0.25 0.31 Peso elemento entre vértice y su Padre. 
0 7 0 4 7 3 7 0 Vértice que dista menos del vértice del árbol 
La suma = 0.21+0.29+0.34+0.46+0.18+0.25+0.31 es la mínima. 
10.5.2. Algoritmo de Kruskal. 
Se inicia con un conjunto vacío los vértices del árbol de mínima cobertura. Luego de ordenados los elementos por sus pesos, se agrega un elemento por vez, los de menor peso se intentan agregar primero. Antes de agregar los vértices del elemento al árbol, se revisa que no se formen circuitos. El procedimiento termina cuando se tengan todos los vértices en el conjunto o se hayan agregado (Vértices-1) ramas al árbol.
24 Estructuras de Datos y Algoritmos 
Profesor Leopoldo Silva Bijit 20-01-2010 
Verificar que los vértices agregados, asociados a un elemento, no formen circuitos no es una 
tarea sencilla. Una primera aproximación es definir un arreglo id de vértices que mantenga la 
información booleana de si un vértice ha sido agregado o no. 
La Figura 10.15, muestra las ramas, definidas por sus vértices: (1, 4), (1, 5) y (0, 3), que ya han 
sido agregadas al árbol. Al inicio se marcan todos los vértices con 0, indicando que aún no han 
sido considerados. Entonces si el and de id[v] e id[w] es verdadero no se agrega el elemento 
(u,w) y se considera el siguiente elemento de mayor peso; si es falso se marcan los vértices con 
1, indicando su consideración. 
Los vértices: 2 y 6 aún no han sido agregados, y los elementos considerados forman una foresta 
o un conjunto de subárboles no conectados entre sí. 
1 4 
5 
0 
3 
0 1 2 3 4 5 6 
1 1 0 1 1 1 0 
Figura 10.15. Generación MST. Algoritmo de Kruskal. 
La información anterior no permitiría agregar una rama que conecte, por ejemplo los vértices 3 
y 4; ya que estos vértices ya están marcados. Para superar esto pueden identificarse en el arreglo 
a cual de los subárboles pertenecen los vértices. Se inicia el arreglo foresta con el índice 
correspondiente al vértice; de este modo, al inicio cada vértice queda asociado a un subárbol 
vacío. Al agregar un elemento cuyos dos vértices no pertenezcan a ninguno de los subárboles se 
los marca pertenecientes a un árbol de la foresta; se emplea números para identificar los árboles 
con enteros mayores que el número de vértices. 
Después de agregar el elemento (1, 4) el arreglo foresta queda como se indica en la Figura 
10.15a. 
1 4 
0 1 2 3 4 5 6 
0 7 2 3 7 5 6 
foresta 
Figura 10.15a. Creación de primer árbol de la foresta. 
La condición: (foresta[u] != foresta[w]) toma valor verdadero si el elemento (u, w) no pertenece 
al árbol. 
Si se agrega elemento (1,5), el vértice 5 no pertenece a un subárbol y el vértice 1 pertenece al 
subárbol 7, se marca el vértice 5 como perteneciente al subárbol 7. Esto se ilustra en la Figura 
10.15b.
Grafos 25 
Profesor Leopoldo Silva Bijit 20-01-2010 
1 4 
0 1 2 3 4 5 6 
0 7 2 3 7 7 6 
foresta 
5 
Figura 10.15b. Agrega elemento a subárbol de la foresta. 
Si se agrega el elemento (0, 3), se crea un nuevo subárbol con identificador 8. Resulta la Figura 
10.15c. 
1 4 
0 1 2 3 4 5 6 
8 7 2 8 7 7 6 
foresta 
5 
0 
3 
Figura 10.15c. Agrega elemento a subárbol de la foresta. 
Si se desea agregar el elemento (3, 4), con la información de los subárboles de la foresta, ahora 
es posible efectuarlo. Sin embargo deben unirse los subárboles, esto puede efectuarse cambiado 
los identificadores de uno de los subárboles. Esto se muestra en la Figura 10.15d. 
1 4 
0 1 2 3 4 5 6 
7 7 2 7 7 7 6 
foresta 
5 
0 
3 
Figura 10.15d. Unión de subárboles de la foresta. 
int foresta[VERTICES]; 
static int cntf; 
void UneSubArboles(int p, int q) 
{ int i, t; 
if ( (foresta[p]==p) && (foresta[q]==q) ) 
{foresta[p]=cntf; foresta[q]=cntf; cntf++;} //crea subárbol 
else 
if (foresta[p]<VERTICES) foresta[p]=foresta[q]; //pega p a subárbol q 
else 
if(foresta[q]<VERTICES) foresta[q]=foresta[p]; //pega q a subárbol p 
else 
for(t=foresta[p], i=0; i<VERTICES; i++) // Complejidad O(VERTICES) 
if(foresta[i] == t) foresta[i] = foresta[q]; //une subárbol p al subárbol q
26 Estructuras de Datos y Algoritmos 
Profesor Leopoldo Silva Bijit 20-01-2010 
} 
int NoPertenece(int p, int q) 
{ 
return (foresta[p] != foresta[q]); 
} 
void mstKruskal(Graph G, Edge *e) 
{ 
int i, k, E; 
E=GRAPHedges(e, G); 
qsortnw(e, 0, E-1); //ordena elementos por pesos. 
for(i=0; i<G->V; i++) foresta[i]=i; //crea conjunto de vértices. 
cntf=G->V; 
for (i= 0, k = 0; i < E && k < G->V-1; i++) 
if (NoPertenece( e[i].v, e[i].w )) //Elemento (u,w) no pertenece al mst 
{ 
UneSubArboles(e[i].v, e[i].w); //La unión es O(n) 
mst[k++] = e[i]; //agrega rama al árbol 
} 
} 
En el arreglo mst de elementos, se deja el árbol, se emplea como variable global. 
Edge mst[VERTICES-1]; 
La rutina de ordenamiento quicksort, se modifica, para adecuarla a los tipos de datos del grafo, y para ordenar según el peso: 
void qsortnw(Edge *a, int l, int r) 
{ 
int i=l, j=r; 
Edge temp; 
float piv=a[(l+r)/2].wt; 
do { 
while ( a[i].wt < piv) i++; 
while( piv < a[j].wt) j--; 
if( i<=j) 
{ if (i!=j) {temp=a[i], a[i]= a[j], a[j]=temp;} 
i++; j--; 
} 
} while(i<=j); 
if( l < j) qsortnw( a, l, j); 
if( i < r ) qsortnw( a, i, r); 
} 
La siguiente secuencia prueba la función, e imprime el mínimo árbol de cobertura:
Grafos 27 
Profesor Leopoldo Silva Bijit 20-01-2010 
mstKruskal(Grafo, Elementos); 
for (i = 0; i < Grafo->V-1; i++) 
printf("%2d-%2d =%0.2fn", mst[i].v, mst[i]. w, mst[i].wt); 
Las funciones que manejan conjuntos, se encuentra en las primeras páginas del texto de R. Sedgewick. 
void GRAPHmstKruskal(Graph G, Edge *e) 
{ 
int i, k, E; 
E=GRAPHedges(e, G); //Se crea representación de elementos a partir del grafo. 
qsortnw(e, 0, E-1); //se ordena a los elementos, según el peso. 
UFinit(G->V); //crea conjunto de vértices. 
for (i= 0, k = 0; i < E && k < G->V-1; i++) //se agregan máximo V-1 ramas 
if (!UFfind(e[i].v, e[i].w)) //si v no está conectado a w 
{ 
UFunion(e[i].v, e[i].w); //se agregan vértices al conjunto 
mst[k++] = e[i]; //se agrega rama e al árbol mst 
} 
UFdestroy(); 
} 
En el arreglo id, se identifican los vértices de un grafo 
Se lleva la cuenta del número de nodos de cada subconjunto en sz. 
static int *id, *sz; 
void UFinit(int N) 
{ int i; 
id = (int *) malloc(N*sizeof(int)); 
sz = (int *) malloc(N*sizeof(int)); 
for (i = 0; i < N; i++) 
{ id[i] = i; sz[i] = 1; } 
} 
Figura 10.16. Descripción de conjuntos, mediante arreglos. 
Cada vértice apunta a otro en el mismo subconjunto, sin ciclos. 
Los vértices conectados, de un subconjunto apuntan a la raíz. 
int find(int x) 
{ int i = x; 
while (i != id[i]) i = id[i]; return i; } 
0 1 N-1 1 1 1 id sz
28 Estructuras de Datos y Algoritmos 
Profesor Leopoldo Silva Bijit 20-01-2010 
//Retorna uno si están conectados 
int UFfind(int p, int q) 
{ return (find(p) == find(q)); } 
void UFunion(int p, int q) // O(2*log(N)) R.S. pág 16 
{ int i = find(p), j = find(q); 
if (i == j) return; 
if (sz[i] < sz[j]) //si nodos conectados a i es menor nodos conectados a j 
{ id[i] = j; sz[j] += sz[i]; } //el i se pega al j y nodos de i se acumulan en j 
else { id[j] = i; sz[i] += sz[j]; } //el j se pega al i y nodos de j se acumulan en i 
//for(i=0;i<VERTICES;i++) printf("%d ",id[i]); 
//for(i=0;i<VERTICES;i++) printf("%d ",sz[i]); 
//putchar('n'); 
} 
void UFdestroy(void) 
{ 
free(id); free(sz); 
} 
Un diseño menos eficiente de las rutinas: 
//Retorna uno si p y q ya están conectados 
int UFfind1(int p, int q) 
{ return (id[p] == id[q]); } // O(1) 
//Cada vértice apunta a otro en el mismo subconjunto, sin ciclos. 
//Los vértices conectados, de un subconjunto apuntan a la raíz del subárbol 
void UFunion1(int p, int q, int N) 
{ int t,i; 
for(t=id[p], i=0; i<N; i++) // O(N) 
if(id[i] == t) id[i] = id[q]; //le coloca raíz q a todos los conectados al conjunto p 
} 
10.5. Trayectorias más cortas en grafos orientados. 
La trayectoria más corta es la suma menor de las ponderaciones o pesos de los elementos, a través de una trayectoria orientada. 
Se denominan redes a los grafos orientados con pesos para sus elementos. 
Es preciso modificar las rutinas anteriores para tratar grafos dirigidos con pesos. Se considera ahora con peso cero a los elementos de la diagonal principal, reflejando que existe conexión del vértice con sí mismo. No se aceptan elementos en paralelo, ni elementos con pesos negativos.
Grafos 29 
Profesor Leopoldo Silva Bijit 20-01-2010 
Existen tres tipos de problemas. Uno de ellos es: dados dos vértices encontrar la trayectoria 
orientada más corta entre los vértices, se lo denomina fuente-sumidero. Otro caso: es encontrar 
todos los trayectos más cortos entre un vértice y todos los demás; esto equivale a encontrar un 
árbol (SPT shortest-path-tree) que conecte al vértice con todos los demás vértices que son 
alcanzables, de tal modo que la trayectoria en el árbol sea la más corta dentro de la red. Un 
tercer problema es encontrar todos los pares de rutas más cortas. 
10.5.1. Modificación de las funciones para tratar grafos orientados con pesos. 
La definición del elemento debe entenderse como dirigido de v hacia w. 
Edge EDGE(int v, int w, float wt) 
{ Edge t; 
t.v=v; t.w=w; t.wt=wt; 
return (t); 
} 
v w 
wt 
Figura 10.17. Elemento orientado, con peso. 
Ahora se coloca en la matriz que cada vértice está a distancia cero consigo mismo. 
//Asignación dinámica de arreglo bidimensional 
float **MATRIXfloat(int r, int c, float wt) 
{ int i, j; 
float **t = malloc(r * sizeof(float *)); 
for (i = 0; i < r; i++) 
t[i] = malloc(c * sizeof(float)); 
for (i = 0; i < r; i++) 
for (j = 0; j < c; j++) 
if(i==j) t[i][j] = 0.; else t[i][j] = wt; //se aceptan lazos en cada vértice 
return t; 
} 
void BorreGrafo(Graph G) 
{ int i; 
float **t = G->adj; 
for (i = 0; i < G->V; i++) free(t[i]); 
free(t); 
free(G); 
} 
Graph GRAPHinit(int V) //Crea grafo vacío, sin elementos. Sólo los vértices. 
{ Graph G = malloc(sizeof *G); //crea cabecera del grafo 
G->V = V; G->E = 0; 
G->adj = MATRIXfloat(V, V, maxWT);
30 Estructuras de Datos y Algoritmos 
Profesor Leopoldo Silva Bijit 20-01-2010 
return G; 
} 
void GRAPHinsertE(Graph G, Edge e) //Inserta elemento 
{ 
if (G->adj[e.v][e.w] == maxWT) G->E++; 
G->adj[e.v][e.w] = e.wt; // Sólo el elemento dirigido. 
} 
void GRAPHremoveE(Graph G, Edge e) //Remueve elemento 
{ 
if (G->adj[e.v][e.w] != maxWT) G->E--; 
G->adj[e.v][e.w] = maxWT; 
} 
int GRAPHedges(Edge a[], Graph G) //Crea arreglo a de elementos a partir de G. 
{ int v, w, E = 0; 
for (v = 0; v < G->V; v++) 
for (w = 0; w < G->V; w++) 
if ((G->adj[v][w] != maxWT)&&(G->adj[v][w] != 0)) 
a[E++] = EDGE(v, w, G->adj[v][w]); 
return E; 
} 
//muestra la matriz mediante listas de vértices conectados a cada vértice 
void GRAPHshowL(Graph G) 
{ int i, j; 
printf("%d vértices, %d elementos.n", G->V, G->E); 
for (i = 0; i < G->V; i++) 
{ printf("%d: ", i); 
for (j = 0; j < G->V; j++) 
if (G->adj[i][j] != maxWT) printf(" %2d-%0.2f", j, G->adj[i][j]);//dos decimales 
putchar('n'); 
} 
} 
//Muestra Matriz de incidencia. 
void GRAPHshowM(Graph G) 
{ int i, j; 
printf("%d vértices, %d elementos.n", G->V, G->E); printf(" "); 
for (j = 0; j < G->V; j++) printf(" %4d ", j); printf("n"); 
for (i = 0; i < G->V; i++) 
{ 
printf("%2d:", i); 
for (j = 0; j < G->V; j++) 
if (G->adj[i][j] != maxWT) printf(" %0.2f", G->adj[i][j]); else printf(" * "); 
putchar('n'); 
}
Grafos 31 
Profesor Leopoldo Silva Bijit 20-01-2010 
} 
Las siguientes definiciones, permiten crear un grafo de seis vértices y 11 elementos. 
#define VERTICES 6 
#define ELEMENTOS 11 
//Variables 
Graph Grafo; 
Edge Elementos[ELEMENTOS]={{0,1,.41},{1,2,.51},{2,3,.50},{4,3,.36}, 
{3,5,.38},{3,0,.45},{0,5,.29},{5,4,.21}, 
{1,4,.32},{4,2,.32},{5,1,.29} }; 
Se inicia el grafo con: 
Grafo = GRAPHinit(VERTICES); 
Y la inserción de los elementos con sus pesos se logra con: 
for(i=0; i<ELEMENTOS; i++) 
GRAPHinsertE(Grafo, Elementos[i] ); 
La lista de los vértices conectados a cada vértice se logra con: 
GRAPHshowL(Grafo); 
La cual muestra: 
0: 0-0.00 1-0.41 5-0.29 
1: 1-0.00 2-0.51 4-0.32 
2: 2-0.00 3-0.50 
3: 0-0.45 3-0.00 5-0.38 
4: 2-0.32 3-0.36 4-0.00 
5: 1-0.29 4-0.21 5-0.00 
La matriz de la red, se logra con: 
GRAPHshowM(Grafo); 
Que muestra: 
6 vértices, 11 elementos. 
0 1 2 3 4 5 
0: 0.00 0.41 * * * 0.29 
1: * 0.00 0.51 * 0.32 * 
2: * * 0.00 0.50 * * 
3: 0.45 * * 0.00 * 0.38 
4: * * 0.32 0.36 0.00 * 
5: * 0.29 * * 0.21 0.00 
La Figura 10.18 muestra el grafo.
32 Estructuras de Datos y Algoritmos 
Profesor Leopoldo Silva Bijit 20-01-2010 
0 
0.41 
1 
5 
0.29 
2 
0.51 
4 
0.32 
3 
0.50 
0.45 
0.38 
0.32 
0.36 
0.29 
0.21 
Figura 10.18. Grafo orientado. 
Se puede crear un árbol de trayectorias, entre un vértice y el resto, a partir del grafo G, con una 
búsqueda primero en profundidad, invocando a: GRAPHsearchDFS(Grafo); 
static int cnt; 
static int pre[VERTICES]; 
static int padre[VERTICES]; 
static float wt[VERTICES]; //distancia menor de raíz a vértice 
#define Pesoew G->adj[e.v][e.w] 
void dfsR(Graph G, Edge e) 
{ int t; 
pre[e.w] = cnt++; //orden en que recorre el árbol 
padre[e.w] = e.v; //se guarda el vértice v padre de w. 
wt[e.w] = wt[e.v] + Pesoew; //se guarda el peso del elemento v-w más el acumulado 
// desde la raíz a v. 
for (t = 0; t < G->V; t++) 
if (G->adj[e.w][t] != maxWT) 
if (pre[t] == NoVisitado) 
{dfsR(G, EDGE(e.w, t, maxWT)); 
/*printf("%d-%d n", e.w, t);*/ 
} 
} 
La rutina recursiva anterior es llamada por: 
void GRAPHsearchDFS(Graph G) 
{ int v; 
cnt = 0; 
for (v = 0; v < G->V; v++) {pre[v] = NoVisitado; padre[v] = SinPadre; wt[v]=0.;} 
for (v = 0; v < G->V; v++) 
if (pre[v] == NoVisitado) 
dfsR(G, EDGE(v, v, maxWT)); //Si el grafo no es conectado, obtiene foresta. 
} 
La cual genera el siguiente árbol de trayectorias, descrito por un arreglo de padres.
Grafos 33 
Profesor Leopoldo Silva Bijit 20-01-2010 
0 1 2 3 4 5 Vértices. 
0 0 1 2 5 3 Padre del vértice. 
0.00 0.41 0.92 1.42 2.01 1.80 Peso trayecto entre vértice y la raíz. 
0 1 2 3 5 4 Orden en que visita los vértices. 
El árbol DFS generado no es un SPT mínimo. 
0 
0.41 
1 
5 
2 
0.51 
4 
3 
0.50 
0.38 
0.21 
0 
0.41 
1 
5 
0.29 
2 
0.51 
4 
0.32 
3 
0.50 
0.45 
0.38 
0.32 
0.36 
0.29 
0.21 
Figura 10.19. Árbol de trayectorias DFS. 
El árbol de búsqueda primero en extensión es una mejor aproximación, pero tampoco es un 
SPT. 
0 1 2 3 4 5 Vértices. 
0 0 1 2 1 0 Padre del vértice. 
0.00 0.41 0.92 1.42 0.73 0.29 Peso trayecto entre vértice y la raíz. 
0 1 3 5 4 2 Orden en que visita los vértices. 
0 
0.41 
1 
5 
0.29 
2 
0.51 
4 
0.32 
3 
0.50 
0 
0.41 
1 
5 
0.29 
2 
0.51 
4 
0.32 
3 
0.50 
0.45 
0.38 
0.32 
0.36 
0.29 
0.21 
Figura 10.19a. Árbol de trayectorias BFS. 
10.5.2. Algoritmo de Dijkstra. 
Determina un SPT, un árbol con las mínimas trayectorias, desde un vértice a los demás. 
No se aceptan elementos en paralelo, ni elementos con pesos negativos. 
El algoritmo consiste inicialmente en colocar el vértice fuente en el SPT. 
Luego, se agrega un elemento por vez, agregando un vértice. Siempre tomando el elemento que 
tenga el trayecto más corto entre la fuente y el vértice que no está en el SPT.
34 Estructuras de Datos y Algoritmos 
Profesor Leopoldo Silva Bijit 20-01-2010 
Se implementa una solución similar al algoritmo de Prim, pero se van agregando vértices que 
estén a la menor distancia de la raíz. 
Escoger raíz. 
Repetir hasta agregar todos los vértices: 
Encontrar un nodo (sea min) cuya distancia a la raíz sea la menor entre todos los nodos no 
pertenecientes al SPT. 
Marcar ese nodo (sea v) como perteneciente al SPT. 
Repetir para cada w nodo no perteneciente al SPT: 
Si hay conexión entre v y w, y si la distancia del nodo raíz a v más la distancia 
de v a w es menor que la distancia actual de la raíz a w: 
Actualizar la distancia de la raíz a w como la distancia de la raíz a v más 
la distancia de v a w. 
Actualizar el nuevo padre de w 
Actualizar el vértice que está a distancia mínima. 
raíz 
v 
w 
wt[w] 
wt[v] 
Figura 10.20. Distancias en algoritmo de Dijkstra. 
Se requieren tres arreglos para implementar el algortimo: 
static int padre[VERTICES]; 
static int DistaMenosDe[VERTICES]; //vértice que dista menos del vértice del árbol. 
static float wt[VERTICES+1]; //distancia menor de raíz a vértice. Requiere un espacio 
// adicional que se emplea como centinela. 
#define Peso G->adj[v][w] 
#define PesoRuta wt[v] + Peso //distancia de raíz a v, más la del elemento v-w 
#define SinPadre -1 
#define NoVisitado -1 
void sptDijkstra(Graph G, int raiz) 
{ 
//Inicio del espacio de variables 
int v, w, min=raiz, cnt; 
for (v = 0; v < G->V; v++)
Grafos 35 
Profesor Leopoldo Silva Bijit 20-01-2010 
{ padre[v] = SinPadre; //se inician como no visitados todos los vértices 
DistaMenosDe[v] = v; //Al inicio cada vértice dista menos de si mismo. 
wt[v] = VERTICES ; //Peso máximo de los trayectos = número de ramas + 1 
} 
wt[raiz] = 0.; //raíz a distancia cero 
wt[G->V] = VERTICES;//centinela. Requiere una posición adicional con peso máximo. 
//El for externo agrega, uno a uno, los vértices al SPT. 
for (cnt=0; cnt< G->V; cnt++) //agrega todos los vértices. O(V2) 
{ 
v = min; padre[min] = DistaMenosDe[min]; //agrega vértice v al SPT 
//printf(" %d n", min);//orden en que agrega los vértices 
for (w = 0, min = G->V; w < G->V; w++) 
//Al inicio min es un vértice que no existe. 
if (padre[w] == NoVisitado) //si w no está en el SPT 
{ 
if ( (Peso!=maxWT) && (PesoRuta < wt[w]) ) 
//Si hay conexión desde v a w, y 
//Si la distancia del nodo raíz a v más la distancia de v a w es menor que 
// la distancia actual de la raíz a w 
{ wt[w] = PesoRuta; //actualiza distancia de la raíz a w 
DistaMenosDe[w] = v; //salva al padre de w. 
} 
if (wt[w] < wt[min]) min = w; //actualiza el vértice candidato al mínimo 
} 
//Al salir del for interno se tiene el nuevo vértice que se agrega al SPT 
} 
} 
El siguiente llamado genera un shortest path tree, como un arreglo de padres, a partir del grafo G, con fuente o raíz 2. 
sptDijkstra(Grafo, 2); 
0 1 2 3 4 5 Vértices. 
3 5 2 2 5 3 Padre del vértice. 
0.95 1.17 0.00 0.50 1.09 0.88 Peso trayecto entre vértice y raíz. wt[i] 
3 5 2 2 5 3 Vértice que Dista Menos Del vértice del árbol 
La raíz 2 a distancia cero de sí misma.
36 Estructuras de Datos y Algoritmos 
Profesor Leopoldo Silva Bijit 20-01-2010 
0 1 
5 
2 
4 
3 
0.50 
0.45 
0.38 
0.29 
0.21 
Figura 10.21. SPT, raíz 2. 
El SPT con fuente en el vértice 0 se genera con: sptDijkstra(Grafo, 0). 
0 1 2 3 4 5 Vértices. 
0 0 4 4 5 0 Padre del vértice. 
0.00 0.41 0.82 0.86 0.50 0.29 Peso trayecto entre vértice y raíz. 
0 0 4 4 5 0 Vértice que dista menos del vértice del árbol 
0 
0.41 
1 
5 
0.29 
2 
0.51 
4 
0.32 
3 
0.50 
0.45 
0.38 
0.32 
0.36 
0.29 
0.21 
Figura 10.22. SPT, raíz 0. 
El SPT con fuente en el vértice 1 se genera con: sptDijkstra(Grafo, 1); 
0 1 2 3 4 5 Vértices. 
3 1 1 4 1 3 Padre del vértice. 
1.13 0.00 0.51 0.68 0.32 1.06 Peso trayecto entre vértice y raíz. 
sptDijkstra(Grafo, 5), produce: 
0 1 2 3 4 5 Vértices. 
3 5 4 4 5 5 Padre del vértice. 
1.02 0.29 0.53 0.57 0.21 0.00 Peso trayecto entre vértice y raíz. 
sptDijkstra(Grafo, 4), produce: 
0 1 2 3 4 5 Vértices. 
3 5 4 4 4 3 Padre del vértice. 
0.81 1.03 0.32 0.36 0.00 0.74 Peso trayecto entre vértice y raíz.
Grafos 37 
Profesor Leopoldo Silva Bijit 20-01-2010 
Referencias. 
Robert Sedgewick, "Algorithms in C", Third edition, Addison Wesley, 1998.
38 Estructuras de Datos y Algoritmos 
Profesor Leopoldo Silva Bijit 20-01-2010 
Problemas resueltos. 
P10.1. Para el siguiente grafo orientado: 
Determinar el arreglo de pesos del trayecto de peso mínimo entre cada vértice y la raíz, y el arreglo padres del vértice. 
a) Para raíz igual al vértice 4. 
b) Para raíz igual al vértice 2. 
Solución: 
a) 
0 1 2 3 4 Vértices. 
4 0 3 4 4 Padre del vértice. 
0.30 0.70 1.00 0.50 0.00 Peso trayecto entre vértice y raíz. 
b) 
0 1 2 3 4 Vértices. 
4 2 2 1 1 Padre del vértice. 
0.90 0.50 0.00 0.80 0.60 Peso trayecto entre vértice y raíz. 
P10.2. Se tiene un grafo definido por un arreglo de elementos. 
Edge Elementos[ELEMENTOS]={{0,1,.4},{1,2,.5},{2,3,.5},{3,4,.5}, 
{1,4,.1},{0,4,.3},{1,3,.3}}; 
a) Dibujar el grafo. 
b) Determinar el mínimo árbol de cobertura aplicando algoritmo de Kruskal. Indicando el orden en que se eligen las ramas. Dibujar el árbol. 
c) Determinar el mínimo árbol de cobertura aplicando algoritmo de Prim. Indicando el orden en que se agregan los vértices, y los arreglos de padres y de pesos entre el vértice y su padre. Dibujar el árbol. 
d) Modificar la función de Prim para imprimir el orden en que se eligen los vértices. 
Solución. 
0 4 0,3 1 0,4 0,1 3 0,3 0,5 0,5 0,5 2
Grafos 39 
Profesor Leopoldo Silva Bijit 20-01-2010 
a) 
b) Se eligen en el orden: 
1- 4 =0.10 
1- 3 =0.30 
0- 4 =0.30 
2- 3 =0.50 
c) 
Se agregan en orden: 0, 4, 1, 3, 2. 
0 1 2 3 4 Vértices. 
0 4 1 1 0 Padre del vértice. 
1.00 0.10 0.50 0.30 0.30 Peso elemento entre vértice y su Padre. 
d) Se intercala el printf, entre las líneas que se indican. 
……….. 
v = min; st[min] = fr[min];//agrega vértice v al MST 
printf(" %d n", min); 
for (w = 0, min = G->V; w < G->V; w++) 
………. 
Ejercicios propuestos. 
E10.1. 
Partiendo del nodo A, determinar arreglo de padres para: 
Árbol de búsqueda primero en extensión (bfs) 
Árbol de búsqueda primero en profundidad (dfs) 
Árbol de cobertura mínima aplicando algoritmo de Prim. 
0 
4 
0,3 
1 
0,4 
0,1 
3 
0,3 
0,5 
0,5 
0,5 
2 
0 
0,3 4 
1 
0,1 
3 
0,3 0,5 
2 
0 
0,3 4 
1 
0,1 
3 
0,3 0,5 
2
40 Estructuras de Datos y Algoritmos 
Profesor Leopoldo Silva Bijit 20-01-2010 
A 
B 
C 
D 
E 
F 
G 
H 
I 
J 
K 
4 
7 
22 
14 
8 
10 
4 
13 
6 
18 
17 22 
5 
13 
3 
9 
8
Grafos 41 
Profesor Leopoldo Silva Bijit 20-01-2010 
Índice general. 
CAPÍTULO 10 ........................................................................................................................................... 1 
GRAFOS. .................................................................................................................................................... 1 
10.1. DEFINICIONES. ................................................................................................................................. 1 
10.2. REPRESENTACIONES. ....................................................................................................................... 2 
10.2.1. Matriz de adyacencia de los elementos en los vértices. .......................................................... 2 
10.2.2. Matrices estáticas en C. .......................................................................................................... 3 
10.2.3. Matrices dinámicas en C. ........................................................................................................ 4 
Declaración de estructuras de datos para un grafo. .......................................................................................... 4 
Definición de variables. ................................................................................................................................... 4 
10.2.4. Funciones para grafos descritos por su matriz de adyacencias. ............................................. 5 
10.2.4.1. Creación. ........................................................................................................................................... 5 
10.2.4.2. Liberación del espacio. ..................................................................................................................... 6 
10.2.4.3. Definición de un elemento. ............................................................................................................... 6 
10.2.4.4. Inserción de elemento en un grafo. ................................................................................................... 6 
10.2.4.5. Eliminación de elemento. .................................................................................................................. 7 
10.2.4.6. Creación de los elementos. ................................................................................................................ 7 
10.2.4.7. Despliegue de un grafo. Lista de vértices. ......................................................................................... 8 
10.2.4.8. Despliegue de la matriz de incidencias. ............................................................................................ 9 
10.2.4.9. Generación de los elementos a partir de la matriz de incidencias. .................................................... 9 
10.3. TRAYECTORIAS EN GRAFOS. .......................................................................................................... 10 
10.3.1. Búsqueda primero en profundidad. ....................................................................................... 11 
10.3.1.1. Definición de búsqueda primero en profundidad. ........................................................................... 11 
10.3.1.2. Diseño de la operación. ................................................................................................................... 12 
10.3.1.3. Modificación para entender la operación. ....................................................................................... 12 
10.3.1.4. Arreglo de padres. ........................................................................................................................... 13 
10.3.1.5. Uso de stack en búsqueda en profundidad. ..................................................................................... 14 
10.3.2. Búsqueda primero en extensión. ............................................................................................ 15 
10.3.2.1. Definición de la búsqueda primero en extensión............................................................................. 15 
10.3.2.2. Diseño de la operación. ................................................................................................................... 16 
10.4. ÁRBOLES CON PESO. ...................................................................................................................... 17 
Modificación de las funciones para tratar grafos con pesos............................................................. 17 
10.5. MÍNIMO ÁRBOL DE COBERTURA. .................................................................................................... 21 
10.5.1. Algoritmo de Prim. ................................................................................................................ 22 
10.5.2. Algoritmo de Kruskal. ........................................................................................................... 23 
10.5. TRAYECTORIAS MÁS CORTAS EN GRAFOS ORIENTADOS. ................................................................ 28 
10.5.1. Modificación de las funciones para tratar grafos orientados con pesos. .............................. 29 
10.5.2. Algoritmo de Dijkstra. ........................................................................................................... 33 
REFERENCIAS. ........................................................................................................................................ 37 
PROBLEMAS RESUELTOS. ........................................................................................................................ 38 
P10.1. Para el siguiente grafo orientado: ......................................................................................... 38 
P10.2. Se tiene un grafo definido por un arreglo de elementos. ....................................................... 38 
Ejercicios propuestos. ....................................................................................................................... 39 
E10.1. ................................................................................................................................................ 39 
ÍNDICE GENERAL. ................................................................................................................................... 41
42 Estructuras de Datos y Algoritmos 
Profesor Leopoldo Silva Bijit 20-01-2010 
ÍNDICE DE FIGURAS. ................................................................................................................................ 43
Grafos 43 
Profesor Leopoldo Silva Bijit 20-01-2010 
Índice de figuras. 
FIGURA 10.1. ELEMENTO, VÉRTICE, INCIDENCIA........................................................................................... 1 
FIGURA 10.2. ELEMENTOS ORIENTADOS. ...................................................................................................... 2 
FIGURA 10.3. MATRIZ DE ADYACENCIAS. ..................................................................................................... 3 
FIGURA 10.4. MATRIZ DE 2 RENGLONES POR 4 COLUMNAS. .......................................................................... 3 
FIGURA 10.5. FORMA DE ALMACENAMIENTO DE MATRICES. ......................................................................... 3 
FIGURA 10.6. ESTRUCTURA PARA MATRIZ DE ADYACENCIAS. ...................................................................... 5 
FIGURA 10.7. GRAFO DESCRITO POR SUS ELEMENTOS. .................................................................................. 7 
FIGURA 10.8. GRAFO PARA ELEMENTOS DE LA FIGURA 10.7. ........................................................................ 8 
FIGURA 10.9. GENERACIÓN DE LOS ELEMENTOS A PARTIR DE MATRIZ DE ADYACENCIAS. .......................... 10 
FIGURA 10.10. ORDEN DE VISITAS, GENERACIÓN DE LLAMADOS. ............................................................... 11 
FIGURA 10.11. VISUALIZACIÓN DE LOS LLAMADOS, EN PROFUNDIDAD....................................................... 14 
FIGURA 10.12. EJEMPLO PARA BÚSQUEDA PRIMERO EN EXTENSIÓN. .......................................................... 15 
FIGURA 10.13. ORDEN DE VISITAS, GENERACIÓN DE LLAMADOS. EN EXTENSIÓN. ...................................... 16 
FIGURA 10.14. CONJUNTOS EN EL ALGORITMO DE PRIM. ............................................................................ 22 
FIGURA 10.15. GENERACIÓN MST. ALGORITMO DE KRUSKAL. .................................................................. 24 
FIGURA 10.15A. CREACIÓN DE PRIMER ÁRBOL DE LA FORESTA. ................................................................. 24 
FIGURA 10.15B. AGREGA ELEMENTO A SUBÁRBOL DE LA FORESTA. ........................................................... 25 
FIGURA 10.15C. AGREGA ELEMENTO A SUBÁRBOL DE LA FORESTA. ........................................................... 25 
FIGURA 10.15D. UNIÓN DE SUBÁRBOLES DE LA FORESTA. .......................................................................... 25 
FIGURA 10.16. DESCRIPCIÓN DE CONJUNTOS, MEDIANTE ARREGLOS. ......................................................... 27 
FIGURA 10.17. ELEMENTO ORIENTADO, CON PESO. ..................................................................................... 29 
FIGURA 10.18. GRAFO ORIENTADO. ............................................................................................................ 32 
FIGURA 10.19. ÁRBOL DE TRAYECTORIAS DFS. ......................................................................................... 33 
FIGURA 10.19A. ÁRBOL DE TRAYECTORIAS BFS. ....................................................................................... 33 
FIGURA 10.20. DISTANCIAS EN ALGORITMO DE DIJKSTRA. ......................................................................... 34 
FIGURA 10.21. SPT, RAÍZ 2. ........................................................................................................................ 36 
FIGURA 10.22. SPT, RAÍZ 0. ........................................................................................................................ 36

Weitere ähnliche Inhalte

Was ist angesagt?

Generación código intermedio 2
Generación código intermedio 2Generación código intermedio 2
Generación código intermedio 2Humano Terricola
 
Teoría De La Complejidad Algoritmica
Teoría De La Complejidad AlgoritmicaTeoría De La Complejidad Algoritmica
Teoría De La Complejidad AlgoritmicaRolf Pinto
 
Arboles - estructura de datos
Arboles - estructura de datos Arboles - estructura de datos
Arboles - estructura de datos Kaneki04
 
Todos+los+comandos+que+hay+que+saber+para+configurar+un+router
Todos+los+comandos+que+hay+que+saber+para+configurar+un+routerTodos+los+comandos+que+hay+que+saber+para+configurar+un+router
Todos+los+comandos+que+hay+que+saber+para+configurar+un+routerjlzo
 
Función Hash: metodos de división y de medio Cuadrado.
Función Hash: metodos de división y de medio Cuadrado.Función Hash: metodos de división y de medio Cuadrado.
Función Hash: metodos de división y de medio Cuadrado.Ana Castro
 
Introducción al núcleo de las redes de telecomunicaciones (core networks)
Introducción al núcleo de las redes de telecomunicaciones (core networks)Introducción al núcleo de las redes de telecomunicaciones (core networks)
Introducción al núcleo de las redes de telecomunicaciones (core networks)Carlos Hazin
 
Diseño de mapas de memoria
Diseño de mapas de memoriaDiseño de mapas de memoria
Diseño de mapas de memoriaEduardo Abalo
 
Programación 3: Grafos, representación y operaciones
Programación 3: Grafos, representación y operacionesProgramación 3: Grafos, representación y operaciones
Programación 3: Grafos, representación y operacionesAngel Vázquez Patiño
 

Was ist angesagt? (20)

Generación código intermedio 2
Generación código intermedio 2Generación código intermedio 2
Generación código intermedio 2
 
Teoría De La Complejidad Algoritmica
Teoría De La Complejidad AlgoritmicaTeoría De La Complejidad Algoritmica
Teoría De La Complejidad Algoritmica
 
Integridad de base de datos
Integridad de base de datosIntegridad de base de datos
Integridad de base de datos
 
Compiladores, Analisis Lexico, Ejemplo Minilenguaje
Compiladores, Analisis Lexico, Ejemplo MinilenguajeCompiladores, Analisis Lexico, Ejemplo Minilenguaje
Compiladores, Analisis Lexico, Ejemplo Minilenguaje
 
Fundamentos de BD - Unidad 5 algebra relacional
Fundamentos de BD - Unidad 5 algebra relacionalFundamentos de BD - Unidad 5 algebra relacional
Fundamentos de BD - Unidad 5 algebra relacional
 
Algebra relacional
Algebra relacionalAlgebra relacional
Algebra relacional
 
ETL
ETLETL
ETL
 
Taller de Base de Datos - Unidad 7 Conectividad
Taller de Base de Datos - Unidad 7 ConectividadTaller de Base de Datos - Unidad 7 Conectividad
Taller de Base de Datos - Unidad 7 Conectividad
 
Arboles - estructura de datos
Arboles - estructura de datos Arboles - estructura de datos
Arboles - estructura de datos
 
Todos+los+comandos+que+hay+que+saber+para+configurar+un+router
Todos+los+comandos+que+hay+que+saber+para+configurar+un+routerTodos+los+comandos+que+hay+que+saber+para+configurar+un+router
Todos+los+comandos+que+hay+que+saber+para+configurar+un+router
 
Funciones del DBA, SA Y DA
Funciones del DBA, SA Y DAFunciones del DBA, SA Y DA
Funciones del DBA, SA Y DA
 
Fundamentos de BD - unidad 3 modelo relacional
Fundamentos de BD - unidad 3 modelo relacionalFundamentos de BD - unidad 3 modelo relacional
Fundamentos de BD - unidad 3 modelo relacional
 
Función Hash: metodos de división y de medio Cuadrado.
Función Hash: metodos de división y de medio Cuadrado.Función Hash: metodos de división y de medio Cuadrado.
Función Hash: metodos de división y de medio Cuadrado.
 
Complejidad Computacional
Complejidad ComputacionalComplejidad Computacional
Complejidad Computacional
 
Introducción al núcleo de las redes de telecomunicaciones (core networks)
Introducción al núcleo de las redes de telecomunicaciones (core networks)Introducción al núcleo de las redes de telecomunicaciones (core networks)
Introducción al núcleo de las redes de telecomunicaciones (core networks)
 
Mosfet_Introduccion.pptx
Mosfet_Introduccion.pptxMosfet_Introduccion.pptx
Mosfet_Introduccion.pptx
 
Diseño de mapas de memoria
Diseño de mapas de memoriaDiseño de mapas de memoria
Diseño de mapas de memoria
 
Manual 3100f s
Manual 3100f sManual 3100f s
Manual 3100f s
 
Frame relay
Frame relayFrame relay
Frame relay
 
Programación 3: Grafos, representación y operaciones
Programación 3: Grafos, representación y operacionesProgramación 3: Grafos, representación y operaciones
Programación 3: Grafos, representación y operaciones
 

Ähnlich wie grafos estructura de datos

Ähnlich wie grafos estructura de datos (20)

19. Grafos.ppt
19. Grafos.ppt19. Grafos.ppt
19. Grafos.ppt
 
Teoria sobre arboles y grafos, presentacion clave sobre las bases de la intel...
Teoria sobre arboles y grafos, presentacion clave sobre las bases de la intel...Teoria sobre arboles y grafos, presentacion clave sobre las bases de la intel...
Teoria sobre arboles y grafos, presentacion clave sobre las bases de la intel...
 
19 Grafos 1
19 Grafos 119 Grafos 1
19 Grafos 1
 
GRAFOS
GRAFOSGRAFOS
GRAFOS
 
Notebook
Notebook Notebook
Notebook
 
Material de grafos.pdf
Material de grafos.pdfMaterial de grafos.pdf
Material de grafos.pdf
 
Teoria de grafos. introducción
Teoria de grafos. introducciónTeoria de grafos. introducción
Teoria de grafos. introducción
 
Cursos de MATLAB
Cursos de MATLABCursos de MATLAB
Cursos de MATLAB
 
Catedra grafos
Catedra grafosCatedra grafos
Catedra grafos
 
Catedra grafos
Catedra grafosCatedra grafos
Catedra grafos
 
Catedra grafos
Catedra grafosCatedra grafos
Catedra grafos
 
Grafos.ppt
 Grafos.ppt Grafos.ppt
Grafos.ppt
 
Tema grafos
Tema grafosTema grafos
Tema grafos
 
Arreglos c++
Arreglos c++Arreglos c++
Arreglos c++
 
Relaciones y Grafos
Relaciones y GrafosRelaciones y Grafos
Relaciones y Grafos
 
Trabajo de computacion
Trabajo de computacionTrabajo de computacion
Trabajo de computacion
 
Grafos
Grafos Grafos
Grafos
 
Grafos
Grafos Grafos
Grafos
 
Tema 3
Tema 3Tema 3
Tema 3
 
Electrónica: Tutorial de Matlab aplicado
Electrónica: Tutorial de Matlab aplicadoElectrónica: Tutorial de Matlab aplicado
Electrónica: Tutorial de Matlab aplicado
 

Kürzlich hochgeladen

Ley 29783 ALCANCES E INTERPRETACION ----
Ley 29783 ALCANCES E INTERPRETACION ----Ley 29783 ALCANCES E INTERPRETACION ----
Ley 29783 ALCANCES E INTERPRETACION ----AdministracionSSTGru
 
SEMANA 6 MEDIDAS DE TENDENCIA CENTRAL.pdf
SEMANA  6 MEDIDAS DE TENDENCIA CENTRAL.pdfSEMANA  6 MEDIDAS DE TENDENCIA CENTRAL.pdf
SEMANA 6 MEDIDAS DE TENDENCIA CENTRAL.pdffredyflores58
 
Trabajo en altura de acuerdo a la normativa peruana
Trabajo en altura de acuerdo a la normativa peruanaTrabajo en altura de acuerdo a la normativa peruana
Trabajo en altura de acuerdo a la normativa peruana5extraviado
 
Descubrimiento de la penicilina en la segunda guerra mundial
Descubrimiento de la penicilina en la segunda guerra mundialDescubrimiento de la penicilina en la segunda guerra mundial
Descubrimiento de la penicilina en la segunda guerra mundialyajhairatapia
 
01 COSTOS UNITARIOS Y PRESUPUESTO DE OBRA-EXPEDIENTE TECNICO DE OBRA.pptx
01 COSTOS UNITARIOS Y PRESUPUESTO DE OBRA-EXPEDIENTE TECNICO DE OBRA.pptx01 COSTOS UNITARIOS Y PRESUPUESTO DE OBRA-EXPEDIENTE TECNICO DE OBRA.pptx
01 COSTOS UNITARIOS Y PRESUPUESTO DE OBRA-EXPEDIENTE TECNICO DE OBRA.pptxluiscisnerosayala23
 
LIQUIDACION OBRAS PUBLICAS POR CONTRATA.pdf
LIQUIDACION OBRAS PUBLICAS  POR CONTRATA.pdfLIQUIDACION OBRAS PUBLICAS  POR CONTRATA.pdf
LIQUIDACION OBRAS PUBLICAS POR CONTRATA.pdfManuelVillarreal44
 
4.3 Subestaciones eléctricas componentes principales .pptx
4.3 Subestaciones eléctricas componentes principales .pptx4.3 Subestaciones eléctricas componentes principales .pptx
4.3 Subestaciones eléctricas componentes principales .pptxEfrain Yungan
 
PRIMER Y SEGUNDO TEOREMA DE CASTIGLIANO.pdf
PRIMER Y SEGUNDO TEOREMA DE CASTIGLIANO.pdfPRIMER Y SEGUNDO TEOREMA DE CASTIGLIANO.pdf
PRIMER Y SEGUNDO TEOREMA DE CASTIGLIANO.pdfAuraGabriela2
 
Informe Mensual MARZO DE SUPERVISION.docx
Informe Mensual MARZO DE SUPERVISION.docxInforme Mensual MARZO DE SUPERVISION.docx
Informe Mensual MARZO DE SUPERVISION.docxTAKESHISAC
 
Edificio residencial Becrux en Madrid. Fachada de GRC
Edificio residencial Becrux en Madrid. Fachada de GRCEdificio residencial Becrux en Madrid. Fachada de GRC
Edificio residencial Becrux en Madrid. Fachada de GRCANDECE
 
1. Cap. 4 Carga Axial (1).pdf237374335347
1. Cap. 4 Carga Axial (1).pdf2373743353471. Cap. 4 Carga Axial (1).pdf237374335347
1. Cap. 4 Carga Axial (1).pdf237374335347vd110501
 
CONSTRUCCIONES II - SEMANA 01 - REGLAMENTO NACIONAL DE EDIFICACIONES.pdf
CONSTRUCCIONES II - SEMANA 01 - REGLAMENTO NACIONAL DE EDIFICACIONES.pdfCONSTRUCCIONES II - SEMANA 01 - REGLAMENTO NACIONAL DE EDIFICACIONES.pdf
CONSTRUCCIONES II - SEMANA 01 - REGLAMENTO NACIONAL DE EDIFICACIONES.pdfErikNivor
 
LICENCIA DE CONSTRUCCION, Y EDIFICACIONES RESPECTO A LA LEY 29090.pptx
LICENCIA DE CONSTRUCCION, Y EDIFICACIONES RESPECTO A LA LEY 29090.pptxLICENCIA DE CONSTRUCCION, Y EDIFICACIONES RESPECTO A LA LEY 29090.pptx
LICENCIA DE CONSTRUCCION, Y EDIFICACIONES RESPECTO A LA LEY 29090.pptxLucindaMy
 
CUENCAS HIDROGRAFICAS CARACTERIZACION GEOMORFOLOGIAS DE LA CUENTA
CUENCAS HIDROGRAFICAS CARACTERIZACION GEOMORFOLOGIAS DE LA CUENTACUENCAS HIDROGRAFICAS CARACTERIZACION GEOMORFOLOGIAS DE LA CUENTA
CUENCAS HIDROGRAFICAS CARACTERIZACION GEOMORFOLOGIAS DE LA CUENTAvanessaecharry2511
 
CFRD simplified sequence for Mazar Hydroelectric Project
CFRD simplified sequence for Mazar Hydroelectric ProjectCFRD simplified sequence for Mazar Hydroelectric Project
CFRD simplified sequence for Mazar Hydroelectric ProjectCarlos Delgado
 
Historia de la Arquitectura II, 1era actividad..pdf
Historia de la Arquitectura II, 1era actividad..pdfHistoria de la Arquitectura II, 1era actividad..pdf
Historia de la Arquitectura II, 1era actividad..pdfIsbelRodrguez
 
Procedimientos constructivos superestructura, columnas
Procedimientos constructivos superestructura, columnasProcedimientos constructivos superestructura, columnas
Procedimientos constructivos superestructura, columnasAhmedMontaoSnchez1
 
Sistema de Base de Datos para renta de trajes
Sistema de Base de Datos para renta de trajesSistema de Base de Datos para renta de trajes
Sistema de Base de Datos para renta de trajesjohannyrmnatejeda
 
Topografía 1 Nivelación y Carretera en la Ingenierías
Topografía 1 Nivelación y Carretera en la IngenieríasTopografía 1 Nivelación y Carretera en la Ingenierías
Topografía 1 Nivelación y Carretera en la IngenieríasSegundo Silva Maguiña
 

Kürzlich hochgeladen (20)

Ley 29783 ALCANCES E INTERPRETACION ----
Ley 29783 ALCANCES E INTERPRETACION ----Ley 29783 ALCANCES E INTERPRETACION ----
Ley 29783 ALCANCES E INTERPRETACION ----
 
presentación manipulación manual de cargas sunafil
presentación manipulación manual de cargas sunafilpresentación manipulación manual de cargas sunafil
presentación manipulación manual de cargas sunafil
 
SEMANA 6 MEDIDAS DE TENDENCIA CENTRAL.pdf
SEMANA  6 MEDIDAS DE TENDENCIA CENTRAL.pdfSEMANA  6 MEDIDAS DE TENDENCIA CENTRAL.pdf
SEMANA 6 MEDIDAS DE TENDENCIA CENTRAL.pdf
 
Trabajo en altura de acuerdo a la normativa peruana
Trabajo en altura de acuerdo a la normativa peruanaTrabajo en altura de acuerdo a la normativa peruana
Trabajo en altura de acuerdo a la normativa peruana
 
Descubrimiento de la penicilina en la segunda guerra mundial
Descubrimiento de la penicilina en la segunda guerra mundialDescubrimiento de la penicilina en la segunda guerra mundial
Descubrimiento de la penicilina en la segunda guerra mundial
 
01 COSTOS UNITARIOS Y PRESUPUESTO DE OBRA-EXPEDIENTE TECNICO DE OBRA.pptx
01 COSTOS UNITARIOS Y PRESUPUESTO DE OBRA-EXPEDIENTE TECNICO DE OBRA.pptx01 COSTOS UNITARIOS Y PRESUPUESTO DE OBRA-EXPEDIENTE TECNICO DE OBRA.pptx
01 COSTOS UNITARIOS Y PRESUPUESTO DE OBRA-EXPEDIENTE TECNICO DE OBRA.pptx
 
LIQUIDACION OBRAS PUBLICAS POR CONTRATA.pdf
LIQUIDACION OBRAS PUBLICAS  POR CONTRATA.pdfLIQUIDACION OBRAS PUBLICAS  POR CONTRATA.pdf
LIQUIDACION OBRAS PUBLICAS POR CONTRATA.pdf
 
4.3 Subestaciones eléctricas componentes principales .pptx
4.3 Subestaciones eléctricas componentes principales .pptx4.3 Subestaciones eléctricas componentes principales .pptx
4.3 Subestaciones eléctricas componentes principales .pptx
 
PRIMER Y SEGUNDO TEOREMA DE CASTIGLIANO.pdf
PRIMER Y SEGUNDO TEOREMA DE CASTIGLIANO.pdfPRIMER Y SEGUNDO TEOREMA DE CASTIGLIANO.pdf
PRIMER Y SEGUNDO TEOREMA DE CASTIGLIANO.pdf
 
Informe Mensual MARZO DE SUPERVISION.docx
Informe Mensual MARZO DE SUPERVISION.docxInforme Mensual MARZO DE SUPERVISION.docx
Informe Mensual MARZO DE SUPERVISION.docx
 
Edificio residencial Becrux en Madrid. Fachada de GRC
Edificio residencial Becrux en Madrid. Fachada de GRCEdificio residencial Becrux en Madrid. Fachada de GRC
Edificio residencial Becrux en Madrid. Fachada de GRC
 
1. Cap. 4 Carga Axial (1).pdf237374335347
1. Cap. 4 Carga Axial (1).pdf2373743353471. Cap. 4 Carga Axial (1).pdf237374335347
1. Cap. 4 Carga Axial (1).pdf237374335347
 
CONSTRUCCIONES II - SEMANA 01 - REGLAMENTO NACIONAL DE EDIFICACIONES.pdf
CONSTRUCCIONES II - SEMANA 01 - REGLAMENTO NACIONAL DE EDIFICACIONES.pdfCONSTRUCCIONES II - SEMANA 01 - REGLAMENTO NACIONAL DE EDIFICACIONES.pdf
CONSTRUCCIONES II - SEMANA 01 - REGLAMENTO NACIONAL DE EDIFICACIONES.pdf
 
LICENCIA DE CONSTRUCCION, Y EDIFICACIONES RESPECTO A LA LEY 29090.pptx
LICENCIA DE CONSTRUCCION, Y EDIFICACIONES RESPECTO A LA LEY 29090.pptxLICENCIA DE CONSTRUCCION, Y EDIFICACIONES RESPECTO A LA LEY 29090.pptx
LICENCIA DE CONSTRUCCION, Y EDIFICACIONES RESPECTO A LA LEY 29090.pptx
 
CUENCAS HIDROGRAFICAS CARACTERIZACION GEOMORFOLOGIAS DE LA CUENTA
CUENCAS HIDROGRAFICAS CARACTERIZACION GEOMORFOLOGIAS DE LA CUENTACUENCAS HIDROGRAFICAS CARACTERIZACION GEOMORFOLOGIAS DE LA CUENTA
CUENCAS HIDROGRAFICAS CARACTERIZACION GEOMORFOLOGIAS DE LA CUENTA
 
CFRD simplified sequence for Mazar Hydroelectric Project
CFRD simplified sequence for Mazar Hydroelectric ProjectCFRD simplified sequence for Mazar Hydroelectric Project
CFRD simplified sequence for Mazar Hydroelectric Project
 
Historia de la Arquitectura II, 1era actividad..pdf
Historia de la Arquitectura II, 1era actividad..pdfHistoria de la Arquitectura II, 1era actividad..pdf
Historia de la Arquitectura II, 1era actividad..pdf
 
Procedimientos constructivos superestructura, columnas
Procedimientos constructivos superestructura, columnasProcedimientos constructivos superestructura, columnas
Procedimientos constructivos superestructura, columnas
 
Sistema de Base de Datos para renta de trajes
Sistema de Base de Datos para renta de trajesSistema de Base de Datos para renta de trajes
Sistema de Base de Datos para renta de trajes
 
Topografía 1 Nivelación y Carretera en la Ingenierías
Topografía 1 Nivelación y Carretera en la IngenieríasTopografía 1 Nivelación y Carretera en la Ingenierías
Topografía 1 Nivelación y Carretera en la Ingenierías
 

grafos estructura de datos

  • 1. 1 Profesor Leopoldo Silva Bijit 20-01-2010 Capítulo 10 Grafos. Los grafos enfatizan las relaciones complejas de conexión entre elementos de datos. Los árboles son un caso simple de grafos. Los grafos permiten estudiar problemas como: menor distancia o costo entre puntos; búsquedas a través de enlaces en la web; redes eléctricas, cableado de impresos; itineración de tareas, siguiendo trayectorias; sintonizar o relacionar postulantes con ofertas, partes con proveedores; conectividad entre pares de sitios en la red; especificación de aplicaciones mostrando las relaciones entre subsistemas; etc. 10.1. Definiciones. Quedan definidos por un conjunto de vértices V, más un conjunto de elementos E que conectan pares de vértices distintos. El grafo se describe por: G(V, E). Figura 10.1. Elemento, vértice, incidencia. Grado de incidencia, del vértice, es el número de elementos que son incidentes en ese vértice. En grafos simples se aceptan sólo un enlace entre un par de vértices diferentes. Es decir, no se aceptan lazos (elementos adyacentes en el mismo vértice), ni elementos en paralelo (dos elementos que son adyacentes con un mismo par de vértices. Trayectoria: Secuencia de elementos entre vértices adyacentes. Trayectoria simple: los vértices y los elementos están presentes sólo una vez. Circuito o ciclo: Trayectoria simple, salvo que el vértice inicial y final son uno solo. Largo de la trayectoria es el número de elementos que la componen. Grafo conectado: Si existe trayectoria entre cualquier par de vértices. Árbol de un grafo: Subgrafo conectado sin circuitos. vértice elemento Vértices adyacentes Elemento incidente en vértice
  • 2. 2 Estructuras de Datos y Algoritmos Profesor Leopoldo Silva Bijit 20-01-2010 Densidad es el promedio de los grados de incidencia de los vértices. Si sumamos los grados de incidencia se tendrá el valor 2E (se cuenta dos veces cada elemento). Entonces la densidad resulta: 2E/V. Se dice que un grafo es denso si su densidad en proporcional a V. Esto implica que el número de elementos E será proporcional a V2. En caso contrario es un grafo liviano (sparse). El concepto de densidad tiene relación con el tipo de algoritmo y la estructura de datos que se empleará para representar el grafo. Complejidades típicas son V2 y E*logE, la primera es más adecuada a grafos densos; y la segunda a grafos livianos. Grafos dirigidos: Los elementos tienen asociada una dirección. Figura 10.2. Elementos orientados. Grafos ponderados: Los elementos ( w(E) ) o los vértices tienen asociado una distancia o costo. Árbol de cobertura (Spanning tree) de un grafo conectado, con pesos y no dirigido, es un árbol cuyo peso se calcula según: E T w(T) w(E) De los innumerables problemas asociados a grafos se estudiarán dos. El mínimo árbol de cobertura, en grafos con peso pero no dirigidos. La ruta mínima: en grafos con peso y dirigidos. 10.2. Representaciones. Para grafos densos se prefiere la matriz de adyacencias, para grafos livianos suele emplearse una lista de adyacencias. 10.2.1. Matriz de adyacencia de los elementos en los vértices. Se emplea una matriz cuadrada (V-1, V-1) donde V es el número de vértices. a Elemento= (a, b) b a Elemento= (b, a) b
  • 3. Grafos 3 Profesor Leopoldo Silva Bijit 20-01-2010 m[0][0] m[0][3] m[1][0] m[1][3] a 0 1 .. w .. V-1 0 1 .. v 1 .. V-1 Figura 10.3. Matriz de adyacencias. Se coloca un 1 en (v, w) si existe elemento de v hacia w; 0 en caso contrario. La matriz es simétrica si el grafo no es dirigido. Si no se aceptan lazos, la diagonal está formada por ceros. 10.2.2. Matrices estáticas en C. La notación: a[v][w] recuerda que la matriz a se visualiza como un arreglo de v renglones, donde cada renglón está formado por w columnas. Ejemplo: La matriz m se define según: int m[2][4]; 0 2 x 4 Figura 10.4. Matriz de 2 renglones por 4 columnas. Se almacenan por renglones. La siguiente figura ilustra el almacenamiento de los elementos del arreglo, con direcciones crecientes hacia abajo: Figura 10.5. Forma de almacenamiento de matrices. Índice más derechista varía más rápido, si se accesa según el orden de almacenamiento. Se inicializan mediante listas: int m[2][4]={{0,1,2,3},{4,5,6,7}};
  • 4. 4 Estructuras de Datos y Algoritmos Profesor Leopoldo Silva Bijit 20-01-2010 Matrices como argumentos. Si se pasa una matriz a una función, la declaración del argumento debe incluir la dimensión de la columna. La dimensión del renglón es irrelevante, ya que se pasa un puntero. f(int m[ ] [4]) ó f(int (*m)[4]) 10.2.3. Matrices dinámicas en C. Ver Sedgewick, cap. 17. Las siguientes declaraciones describen un grafo a través de una matriz de adyacencias. Declaración de estructuras de datos para un grafo. struct graph { int V; //Número de vértices int E; //Número de elementos int **adj; // Matriz de adyacencias }; typedef struct graph *Graph; Definición de variables. Las siguientes definiciones crean el espacio. Se define un grafo G, cuya matriz de adyacencias se define según un arreglo de r punteros para almacenar las direcciones de los arreglos de c elementos: Graph G = malloc(sizeof *G); //crea la cabecera del grafo. int **t = malloc(r * sizeof(int *)); //crea arreglo de r renglones de punteros G->adj = t; //Pega el arreglo de punteros //crea y pega los renglones de c columnas: for (i = 0; i < r; i++) t[i] = malloc(c * sizeof(int)); El siguiente diagrama ilustra las variables. Se ha omitido la casilla asociada a t.
  • 5. Grafos 5 Profesor Leopoldo Silva Bijit 20-01-2010 Figura 10.6. Estructura para matriz de adyacencias. También se puede definir una matriz estática (sin llamar a malloc()) : como un arreglo de r punteros que apuntan a arreglos con la información de las columnas. 10.2.4. Funciones para grafos descritos por su matriz de adyacencias. La función creación de un grafo vacío de V vértices: 10.2.4.1. Creación. Graph GRAPHinit(int V) { Graph G = malloc(sizeof *G); //crea cabecera del grafo G->V = V; G->E = 0; //Con V vértices y 0 elementos G->adj = MATRIXint(V, V, 0); //Lo inicia con ceros. return G; } Donde MATRIXint crea la matriz de incidencias de r renglones, c columnas y la inicializa con val. //Asignación dinámica de arreglo bidimensional int **MATRIXint(int r, int c, int val) { int i, j; int **t = malloc(r * sizeof(int *)); for (i = 0; i < r; i++) t[i] = malloc(c * sizeof(int)); // t[i] equivale a *(t+i) for (i = 0; i < r; i++) for (j = 0; j < c; j++) t[i][j] = val; //equivale a **(t+i+j) = val; return t; j i t t[i] ó *(t+i) t[i][j] ó **(t+i+j) G V E adj
  • 6. 6 Estructuras de Datos y Algoritmos Profesor Leopoldo Silva Bijit 20-01-2010 } De complejidad: O(r + r*c) La siguiente función, libera el espacio asociado al grafo, cuidando de borrar en orden inverso al solicitado, de tal modo de no perder las referencias. 10.2.4.2. Liberación del espacio. void BorreGrafo(Graph G) { int i; int **t = G->adj; for (i = 0; i < G->V; i++) free(t[i]); //primero borra los renglones free(t); //luego el arreglo de punteros a los renglones free(G); //finalmente la cabecera } Si se tiene la definición: Graph Grafo; Puede crearse la matriz de adyacencias del grafo mediante: #define VERTICES 5 Grafo = GRAPHinit(VERTICES); 10.2.4.3. Definición de un elemento. Un elemento puede describirse por: typedef struct { int v; //vértice inicial. Desde. int w; //vértice final. Hasta. } Edge; Con el siguiente constructor: Edge EDGE(int v, int w) { Edge t; t.v=v; t.w=w; return (t); } La siguiente definición crea un elemento, y EDGE lo inicializa. Edge elemento; elemento = EDGE(1,2); 10.2.4.4. Inserción de elemento en un grafo. La siguiente función inserta un elemento e en un grafo G.
  • 7. Grafos 7 Profesor Leopoldo Silva Bijit 20-01-2010 void GRAPHinsertE(Graph G, Edge e) { if (G->adj[e.v][e.w] == 0) G->E++; //aumenta el número de elementos del grafo G->adj[e.v][e.w] = 1; G->adj[e.w][e.v] = 1; //si el grafo no es dirigido. } 10.2.4.5. Eliminación de elemento. La función siguiente remueve el elemento e del grafo G: void GRAPHremoveE(Graph G, Edge e) { if (G->adj[e.v][e.w] == 1) G->E--; //Disminuye el contador de elementos. G->adj[e.v][e.w] = 0; G->adj[e.w][e.v] = 0; } La acción siguiente, inserta el elemento en el Grafo: GRAPHinsertE(Grafo, elemento); 10.2.4.6. Creación de los elementos. La siguiente definición crea el conjunto de elementos de un grafo, como un arreglo denominado Elementos. Esta es otra forma de definir un grafo. Requiere 2E datos para ser definida, en lugar de V2 que necesita la matriz de incidencia. #define ELEMENTOS 6 Edge Elementos[ELEMENTOS]={{1,2},{1,4},{2,3},{3,4},{4,0},{3,0} }; elemento v w 0 1 2 1 1 4 2 2 3 3 3 4 4 4 0 5 3 0 Figura 10.7. Grafo descrito por sus elementos. Esta descripción es única, en el sentido que asocia nombres de elementos con nombres de vértices. Entonces la creación de la matriz de incidencia de un grafo de cinco vértices, a partir del arreglo de elementos, definido por seis elementos, puede realizarse, según:
  • 8. 8 Estructuras de Datos y Algoritmos Profesor Leopoldo Silva Bijit 20-01-2010 for(i=0; i<ELEMENTOS; i++) GRAPHinsertE(Grafo, Elementos[i] ); que tiene complejidad O(E). Mostrando que la matriz de incidencia puede obtenerse del arreglo de los elementos, lo cual indica que son representaciones equivalentes. El grafo resultante, puede visualizarse, según: Figura 10.8. Grafo para elementos de la figura 10.7. 10.2.4.7. Despliegue de un grafo. Lista de vértices. Para visualizar el grafo, puede generarse una lista de los vértices: con los vértices que tienen conectados elementos a ese vértice. //muestra la matriz como listas de vértices que tienen conexión con cada vértice. void GRAPHshowL(Graph G) { int i, j; printf("%d vertices, %d edgesn", G->V, G->E); //número total de elementos y vértices. for (i = 0; i < G->V; i++) { printf("%2d:", i); for (j = 0; j < G->V; j++) if (G->adj[i][j] == 1) printf(" %2d", j); putchar(‘n’); } } De complejidad O(V2). El cual entregaría el siguiente listado. 5 vertices, 6 edges 0: 3 4 1: 2 4 2: 1 3 3: 0 2 4 4: 0 1 3 0 3 4 1 2 0 1 2 5 3 4
  • 9. Grafos 9 Profesor Leopoldo Silva Bijit 20-01-2010 10.2.4.8. Despliegue de la matriz de incidencias. //Muestra Matriz de incidencia. void GRAPHshowM(Graph G) { int i, j; printf("%d vertices, %d edgesn", G->V, G->E); printf(" "); for (j = 0; j < G->V; j++) printf(" %2d", j); putchar(‘n’); for (i = 0; i < G->V; i++) { printf("%2d:", i); for (j = 0; j < G->V; j++) if (G->adj[i][j] == 1) printf(" %2d", 1); else printf(" %2d", 0); putchar(‘n’); } } Que genera, para el ejemplo: 5 vertices, 6 edges 0 1 2 3 4 0: 0 0 0 1 1 1: 0 0 1 0 1 2: 0 1 0 1 0 3: 1 0 1 0 1 4: 1 1 0 1 0 10.2.4.9. Generación de los elementos a partir de la matriz de incidencias. Si el grafo ya está construido, la generación de los elementos, a partir del grafo, se logra con la función: int GRAPHedges(Edge a[], Graph G) { int v, w, E = 0; //numera los elementos desde el cero. for (v = 0; v < G->V; v++) //Para todos los renglones for (w = v+1; w < G->V; w++) //revisa por columnas if (G->adj[v][w] == 1) a[E++] = EDGE(v, w); //escribe por referencia return E; //retorna el número de elementos. } Se advierte que debido a los dos for anidados es O( (V2-V)/2 ), ya que revisa sobre la diagonal. Encontrar los elementos a partir del grafo se realiza con: GRAPHedges(Elementos, Grafo); //Llena el arreglo a partir del Grafo. Nótese que al recorrer la submatriz sobre la diagonal, por renglones, va reasignando, a partir de cero, los nombres de los elementos, y sus correspondientes vértices.
  • 10. 10 Estructuras de Datos y Algoritmos Profesor Leopoldo Silva Bijit 20-01-2010 elemento v w 0 0 3 1 0 4 2 1 2 3 1 4 4 2 3 5 3 4 Figura 10.9. Generación de los elementos a partir de matriz de adyacencias. Debido a que la matriz de adyacencias no almacena información sobre el nombre de los elementos, el arreglo de elementos toma los nombres dados por el recorrido. 10.3. Trayectorias en grafos. Un primer problema es determinar si existe una trayectoria entre dos vértices. Si se define un arreglo en que se marque si los nodos han sido o no visitados, puede plantearse el siguiente esquema recursivo: Si el vértice inicial y el final son iguales, hay trayectoria (fin de recursión). Marcar el vértice inicial como visitado. Revisar todos los vértices, conectados al inicial: Si uno no ha sido revisado: ver si hay trayectoria entre ese y el final. Si revisados todos no hay trayectoria, entonces no existe la trayectoria buscada. int pathR(Graph G, int v, int w) { int t; if (v == w) return 1; //Existe trayecto. Nodo inicial y final son iguales. visited[v] = 1; for (t = 0; t < G->V; t++) if (G->adj[v][t] == 1) //Si v está conectado con t if (visited[t] == 0) //y t no ha sido visitado { printf("%d-%d ", v, t); //Debug: Muestra elemento de trayectoria en prueba. if (pathR(G, t, w)) return 1; } return 0; } 0 3 4 1 2 2 3 4 0 5 1 0 1 2 3 4 0 0 0 0 1 1 1 0 0 1 0 1 2 0 1 0 1 0 3 1 0 1 0 1 4 1 1 0 1 0
  • 11. Grafos 11 Profesor Leopoldo Silva Bijit 20-01-2010 int GRAPHpath(Graph G, int v, int w) { int t; for (t = 0; t < G->V; t++) visited[t] = 0; return pathR(G, v, w); //Inicia búsqueda recursiva. } Un ejemplo de invocación: if( GRAPHpath(Grafo, 1, 3)) printf("existe trayectoria, entre 1 y 3n"); else printf("no existe trayectoria.n"); Existen dos formas básicas de exploración de un grafo: La búsqueda primero en profundidad (Depth-first search) y la búsqueda primero en extensión (Breadth –first search). 10.3.1. Búsqueda primero en profundidad. 10.3.1.1. Definición de búsqueda primero en profundidad. Se recorre el grafo, siempre hacia adelante hasta que se llega al final o hasta una trayectoria que ya se ha recorrido; luego se devuelve y busca trayectorias no exploradas. El objetivo es buscar un árbol. Se visita y marca un vértice como visitado, luego se visitan (recursivamente) todos los vértices adyacentes al recién marcado que no estén visitados. Esto va formando un árbol, con el orden en que se van visitando los vértices. Para el grafo a la izquierda de la Figura 10.10.: Si el vértice inicial es el 0 (se pasa el elemento (0,0), se lo visita y marca con la cuenta 0, y se generan llamados con los elementos (0,3) y (0,4), en ese orden. Figura 10.10. Orden de visitas, generación de llamados. El llamado con el elemento (0, 3) marca el 3 con la cuenta 1, y se generan los llamados: con (3, 2), (3,4). El llamado con el elemento (3, 2) marca el 2 con la cuenta 2, y se genera el llamado: (2,1). El llamado con el elemento (2, 1) marca el 1 con la cuenta 3, y se genera el llamado: (1,4). El llamado con el elemento (1, 4) marca el 4 con la cuenta 4 y no genera nuevos llamados. Resultando el árbol T(1, 2, 3, 4) ilustrado en la figura a la derecha. 0 3 4 1 2 0 3 4 1 3 2 4 2 1
  • 12. 12 Estructuras de Datos y Algoritmos Profesor Leopoldo Silva Bijit 20-01-2010 10.3.1.2. Diseño de la operación. La función recursiva: void dfsR(Graph G, Edge e) { int t, w = e.w; pre[w] = cnt++; //Marca con contador creciente for (t = 0; t < G->V; t++) { if (G->adj[w][t] != 0) //Si hay conexión entre w y t if (pre[t] == -1) dfsR(G, EDGE(w, t)); //Y si t no está visitado sigue buscando. } } La función que efectúa la búsqueda en profundidad: void GRAPHsearchDFS(Graph G) //depth-first-search { int v; cnt = 0; //es variable global for (v = 0; v < G->V; v++) pre[v] = -1; //Marca todos como no visitados for (v = 0; v < G->V; v++) //Revisa todos los vértices. if (pre[v] == -1) dfsR(G, EDGE(v, v)); //Se invoca varias veces si el grafo es no conectado. } Se visitan todos los elementos y todos los vértices conectados al vértice de partida, no importando el orden en que revisa los elementos incidentes en ese vértice. La estrategia recursiva implica un orden: el último que entró es el primero que salió (LIFO), de los elementos posibles se elige el más recientemente encontrado. 10.3.1.3. Modificación para entender la operación. Se agrega la variable estática indente, para ir mostrando los elementos que son sometidos a revisión. Cada vez que se genera un llamado se produce un mayor nivel de indentación; el cual es repuesto al salir del llamado recursivo. Se agrega un asterisco para mostrar los elementos que generan llamados recursivos. static int indente=0; void dfsR(Graph G, Edge e) { int t,j, w = e.w; pre[w] = cnt++; for (t = 0; t < G->V; t++) { if (G->adj[w][t] != 0) { for (j = 0; j < indente; j++) printf(" "); printf("%d-%d n", w, t); if (pre[t] == -1) { indente++; putchar('*'); dfsR(G, EDGE(w, t));
  • 13. Grafos 13 Profesor Leopoldo Silva Bijit 20-01-2010 indente--; } else putchar(' '); } } } Para el grafo del ejemplo de la figura 10.10., se produce el listado: 0-3 * 3-0 3-2 * 2-1 * 1-2 1-4 * 4-0 4-1 4-3 2-3 3-4 0-4 Este algoritmo puede emplearse: para detectar circuitos, para saber si existe una trayectoria que conecte a dos vértices dados, para determinar si es un grafo conectado, para encontrar un árbol. Ver propiedades de los árboles dfs en Sedgewick 18.4. 10.3.1.4. Arreglo de padres. La siguiente modificación permite almacenar en el arreglo st el padre del vértice w. Esa información es útil para aplicaciones de este algoritmo. Y es una forma de describir el árbol. static int st[VERTICES]; void dfsR(Graph G, Edge e) { int t, j, w = e.w; pre[w] = cnt++; //Marca con contador creciente st[e.w] = e.v; //Se guarda el padre de w. for (t = 0; t < G->V; t++) { if (G->adj[w][t] != 0) //Si hay conexión entre w y t if (pre[t] == -1) dfsR(G, EDGE(w, t)); //Y t no está visitado busca t. } } Para el ejemplo, quedarían almacenados en st: 0, 2, 3, 0, 1. Y en pre: 0, 3, 2, 1, 4. El árbol que genera este algoritmo, se produce revisando los elementos en el siguiente orden:
  • 14. 14 Estructuras de Datos y Algoritmos Profesor Leopoldo Silva Bijit 20-01-2010 0-3 , *3-0 , 3-2, *2-1, *1-2, 1-4, *4-0, 4-1, 4-3, 2-3, 3-4, 0-4. Los que generan llamados recursivos, se preceden con un asterisco. La ilustración muestra que se avanza primero en profundidad. 0 3 4 0 2 4 1 3 2 4 0 1 3 Figura 10.11. Visualización de los llamados, en profundidad. 10.3.1.5. Uso de stack en búsqueda en profundidad. Se emplea un stack para guardar los vértices. //árbol en profundidad. No recursivo void dfs(Graph G, Edge e) { int t, v, w, f; StackInit(G->V*2); StackPush(e.v); StackPush(e.w); //empuja los vértices del elemento while(!StackEmpty()) { w=StackPop(); v=StackPop(); StackPush(v); f=1; //guarda ruta en el árbol pre[w] = cnt++; st[w] = v; // se guarda el vértice padre de w. while(f) { for (t = 0; t < G->V; t++) { if (G->adj[w][t] != 0) if (pre[t] == -1) { StackPush(w); StackPush(t); printf("*%d-%d n", w, t); f=0; break; } }
  • 15. Grafos 15 Profesor Leopoldo Silva Bijit 20-01-2010 if (f) //llegó al final de un recorrido en el árbol. {if (!StackEmpty()) w=StackPop(); //extrae vértice anterior del árbol else f=0; } } } Stackdestroy(); } void GRAPHsearchDFSnr(Graph G) { int v; cnt = 0; for (v = 0; v < G->V; v++) {pre[v] = -1; st[v] = -1;} for (v = 0; v < G->V; v++) if (pre[v] == -1) dfs(G, EDGE(v, v)); } 10.3.2. Búsqueda primero en extensión. 10.3.2.1. Definición de la búsqueda primero en extensión. La búsqueda en extensión tiene por objetivo encontrar la ruta más corta entre dos vértices dados. También se genera un árbol. Se parte de un nodo, y se busca el siguiente nodo en todas las rutas de largo uno que existan; luego se buscan todas las rutas de largo dos, y así sucesivamente. Explorar los vértices, de acuerdo a su distancia al de partida, implica que de los elementos que son posibles, se escoge uno y los otros se salvan para ser posteriormente explorados. Este orden es: el primero que se encuentra, es el primero en ser procesado (FIFO). Para visitar un vértice: se buscan los elementos que son incidentes con ese vértice, y los elementos que tienen el otro vértice no visitado, se encolan. Para el siguiente grafo: Figura 10.12. Ejemplo para búsqueda primero en extensión. 0 3 4 1 2
  • 16. 16 Estructuras de Datos y Algoritmos Profesor Leopoldo Silva Bijit 20-01-2010 A partir del nodo inicial 0, se marca el 0, y se exploran los elementos: 0-3 y 0-4. Se encolan el 0-3 y el 0-4. Ya que el 3 y 4 no han sido visitados aún. Se desencola el 0-3, se marca el 3, y se revisan el 3-0, el 3-2 y el 3-4. Se encolan el 3-2 y el 3-4. Ya que el 2 y el 4 no han sido visitados aún. Se desencola el 0-4, se marca el 4, y se revisan el 4-0, 4-1 y 4-3. Se encola el 4-1. Se desencola el 3-2, se marca el 2, y se revisan el 2-1, 2-3. Se encola el 2-1. Se desencola el 3-4 sin procesar. Se desencola el 4-1, se marca el 1, y se revisan el 1-2 y 1-4. Se desencola el 2-1 sin procesar. Del árbol formado, se visualiza, que el vértice 0 está a distancia uno de los vértices 3 y 4, y que está a distancia 2, de los vértices 1 y 2. Los vértices se marcan en el siguiente orden: 0, 3, 4, 2, 1. Figura 10.13. Orden de visitas, generación de llamados. En extensión. La figura10.13. ilustra que se busca por largos de trayectos. 10.3.2.2. Diseño de la operación. El código de la función que encola los elementos. En la cola se almacenan elementos. void bfs(Graph G, Edge e) //breadth-first-search { int v; QUEUEput(e); while (!QUEUEempty()) if (pre[(e = QUEUEget()).w] == -1) { pre[e.w] = cnt++; //en pre queda el orden en que se escogen los vértices st[e.w] = e.v; //en st queda el padre. for (v = 0; v < G->V; v++) if (G->adj[e.w][v] == 1) if (pre[v] == -1) QUEUEput(EDGE(e.w, v)); } } La función que genera el árbol BFS. void GRAPHsearchBFS(Graph G) { int v; 0 3 4 0 2 4 1 3 2 4 0 1 3
  • 17. Grafos 17 Profesor Leopoldo Silva Bijit 20-01-2010 cnt = 0; QUEUEinit(ELEMENTOS); for (v = 0; v < G->V; v++) {pre[v] = -1; st[v]=-1;} //Inicio de estructuras for (v = 0; v < G->V; v++) // Para todos los vértices if (pre[v] == -1) bfs(G, EDGE(v, v)); //Se invoca una vez, si el grafo es conectado. QUEUEdestroy(); } 10.4. Árboles con peso. Si los elementos tienen asociado un peso se desea encontrar un árbol tal que la suma de los pesos de sus ramas sea mínimo. Previo a resolver este problema es necesario modificar las estructuras de datos para incorporar el peso del elemento. Puede escogerse un número real entre 0. y menor que 1. Esto puede lograrse dividiendo los pesos reales por el peso del mayor levemente incrementado. Modificación de las funciones para tratar grafos con pesos. Se elige: typedef struct { int v; //vértice inicial int w; //vértice final float wt; //peso. Puede ser un double } Edge; El constructor queda ahora: Edge EDGE(int v, int w, float wt) { Edge t; t.v=v; t.w=w; t.wt=wt; return (t); } Para un grafo se definen: struct graph { int V; //Número de vértices int E; //Número de elementos float **adj; // Matriz de adyacencias }; typedef struct graph *Graph; Para marcar la no existencia de adyacencias, se define: #define maxWT 1. //Todo elemento tiene menor peso que maxWT.
  • 18. 18 Estructuras de Datos y Algoritmos Profesor Leopoldo Silva Bijit 20-01-2010 La inicialización de un grafo vacío, se logra con: Graph GRAPHinit(int V) { Graph G = malloc(sizeof *G); //crea cabecera del grafo G->V = V; G->E = 0; G->adj = MATRIXfloat(V, V, maxWT); return G; } Donde la función que localiza espacio dinámico para la matriz de adyacencias es: float **MATRIXfloat(int r, int c, float wt) { int i, j; float **t = malloc(r * sizeof(float *)); for (i = 0; i < r; i++) t[i] = malloc(c * sizeof(float)); for (i = 0; i < r; i++) for (j = 0; j < c; j++) t[i][j] = wt; //**(t+i+j) = wt; return t; } El resto de las funciones se modifican para tratar grafos ponderados: void BorreGrafo(Graph G) //Libera el espacio adquirido por malloc. { int i; float **t = G->adj; for (i = 0; i < G->V; i++) free(t[i]); free(t); free(G); } void GRAPHinsertE(Graph G, Edge e) //Inserta elemento { if (G->adj[e.v][e.w] == maxWT) G->E++; G->adj[e.v][e.w] = e.wt; G->adj[e.w][e.v] = e.wt; //suprimir para grafos dirigidos } void GRAPHremoveE(Graph G, Edge e) //Remueve elemento { if (G->adj[e.v][e.w] != maxWT) G->E--; G->adj[e.v][e.w] = maxWT; G->adj[e.w][e.v] = maxWT; //suprimir para grafos dirigidos } int GRAPHedges(Edge a[], Graph G) //Forma arreglo de elementos a partir del grafo { int v, w, E = 0;
  • 19. Grafos 19 Profesor Leopoldo Silva Bijit 20-01-2010 for (v = 0; v < G->V; v++) for (w = v+1; w < G->V; w++) if (G->adj[v][w] != maxWT) a[E++] = EDGE(v, w, G->adj[v][w]); return E; } //muestra la matriz mediante listas de vértices conectados a cada vértice void GRAPHshowL(Graph G) { int i, j; printf("%d vertices, %d edgesn", G->V, G->E); for (i = 0; i < G->V; i++) { printf("%d: ", i); for (j = 0; j < G->V; j++) if (G->adj[i][j] != maxWT) printf(" %0.2f", G->adj[i][j]); putchar('n'); } } //Muestra Matriz de adyacencias. void GRAPHshowM(Graph G) { int i, j; printf("%d vertices, %d edgesn", G->V, G->E); printf(" "); for (j = 0; j < G->V; j++) printf(" %4d ", j); printf("n"); for (i = 0; i < G->V; i++) { printf("%2d:", i); for (j = 0; j < G->V; j++) if (G->adj[i][j] != maxWT) printf(" %0.2f", G->adj[i][j]); else printf(" * "); putchar('n'); } } Se limita a dos el número de cifras significativas. Las siguientes líneas describen un grafo a partir de sus elementos. #define VERTICES 8 #define ELEMENTOS 12 //Variables Graph Grafo; //Edge Elementos[ELEMENTOS]={{0,2,.29},{4,3,.34},{5,3,.18},{7,4,.46},{7,0,.31}, {7,6,.25},{7,1,.21},{0,6,.51},{6,4,.52},{4,5,.40}, {5,0,.59},{0,1,.32} };
  • 20. 20 Estructuras de Datos y Algoritmos Profesor Leopoldo Silva Bijit 20-01-2010 La descripción del grafo, descrito por sus elementos, según la lista de vértices adyacentes resulta: 8 vértices, 12 elementos 0: 0.32 0.29 0.59 0.51 0.31 1: 0.32 0.21 2: 0.29 3: 0.34 0.18 4: 0.34 0.40 0.52 0.46 5: 0.59 0.18 0.40 6: 0.51 0.52 0.25 7: 0.31 0.21 0.46 0.25 La matriz de adyacencias con los pesos, resulta: 0 1 2 3 4 5 6 7 0: * 0.32 0.29 * * 0.59 0.51 0.31 1: 0.32 * * * * * * 0.21 2: 0.29 * * * * * * * 3: * * * * 0.34 0.18 * * 4: * * * 0.34 * 0.40 0.52 0.46 5: 0.59 * * 0.18 0.40 * * * 6: 0.51 * * * 0.52 * * 0.25 7: 0.31 0.21 * * 0.46 * 0.25 * El siguiente ejemplo muestra las modificaciones a la generación de un árbol, empleando búsqueda primero en profundidad. Se agrega el vector wt con los pesos. static int cnt; static int pre[VERTICES]; static int st[VERTICES]; static float wt[VERTICES]; void dfsR(Graph G, Edge e) { int t; pre[e.w] = cnt++; st[e.w] = e.v; //se guarda el vértice v padre de w. wt[e.w]=G->adj[e.w][e.v]; //se guarda el peso del elemento w-v for (t = 0; t < G->V; t++) { if (G->adj[e.w][t] != maxWT) if (pre[t] == -1) {dfsR(G, EDGE(e.w, t, maxWT)); /*printf("%d-%d n", e.w, t);*/} } }
  • 21. Grafos 21 Profesor Leopoldo Silva Bijit 20-01-2010 void GRAPHsearchDFS(Graph G) { int v; cnt = 0; for (v = 0; v < G->V; v++) {pre[v] = -1; st[v] = -1;} //Inicialización for (v = 0; v < G->V; v++) if (pre[v] == -1) dfsR(G, EDGE(v, v, maxWT)); } La cual entrega en st la descripción del árbol a partir de un arreglo de padres de los vértices. 0 1 2 3 4 5 6 7 Vértices. 0 0 0 4 7 3 4 1 Padre del vértice. 1.0 0.32 0.29 0.34 0.46 0.18 0.52 0.21 Peso del elemento entre vértice y su Padre. La raíz del árbol (vértice 0) tiene un lazo de peso infinito (valor 1.0) consigo misma. Este árbol cubre todos los nodos, pero no es un árbol de cobertura mínima. 10.5. Mínimo árbol de cobertura. Para un grafo dado existe un muy elevado número de árboles. No es fácil encontrar el árbol de cobertura mínima. Algunos conceptos para definir el árbol de cobertura mínima: Un elemento del árbol se denomina rama. Si se agrega un elemento a un árbol se crea un único circuito, el elemento agregado suele denominarse cuerda. Un circuito fundamental está formado por una cuerda y el resto de los elementos deben ser ramas. Un conjunto de corte son los elementos que unen dos conjuntos disjuntos de vértices. Uno de los elementos del conjunto de corte debe ser rama. Si los elementos tienen pesos diferentes, la rama del conjunto de corte debe tener peso mínimo. Si existen múltiples elementos mínimos en el corte, al menos uno de ellos debe estar presente. Entonces: Cada rama de un mínimo árbol de cobertura es el elemento mínimo de un conjunto de corte. Así también las cuerdas deben tener los máximos pesos de los circuitos que formen con el mínimo árbol de cobertura. La formación de un MST (minimum spanning tree) consiste en aplicar las reglas anteriores, para rechazar elementos que sean cuerdas, con peso máximo; y aceptar ramas, con pesos mínimos. El algoritmo de Prim, elige un vértice cualquiera como inicial.
  • 22. 22 Estructuras de Datos y Algoritmos Profesor Leopoldo Silva Bijit 20-01-2010 Luego repite para los (V-1) vértices restantes: Agregar elemento de peso mínimo que conecte los vértices del MST, con los vértices que aún no pertenecen al MST. El algoritmo de Kruskal procesa los elementos ordenados por sus pesos. Se va agregando al MST un elemento que no forme circuitos con los previamente agregados, y se detiene si se han agregado (V-1) elementos. Esto porque en grafos conectados el árbol tiene (V-1) ramas. 10.5.1. Algoritmo de Prim. Si se tienen vértices en el MST, se busca un vértice w que aún no está en el árbol, tal que esté a menor distancia de los vértices del MST. Para esto es preciso registrar las menores distancias, de cada nodo no perteneciente al MST, a los nodos pertenecientes al MST; y elegir la menor. Figura 10.14. Conjuntos en el algoritmo de Prim. Para diseñar el algoritmo se emplean tres arreglos: static int padre[VERTICES]; // padre del vértice static int DistaMenosDe[VERTICES]; //vértice que dista menos del vértice del árbol. static float wt[VERTICES+1]; //distancia menor. Con espacio para centinela. El árbol se describe por el arreglo padre[v], donde se almacena el padre del vértice v. Se inicia con valores iguales a menos uno, para indicar que ningún vértice forma parte del árbol. Se agrega un arreglo DistaMenosDe[w], en el cual, durante el procesamiento, se dejará el vértice más cercano a w del árbol. Se inicia con DistaMenosDe[i] = i, indicando que esta información no se conoce. Antes se empleó un arreglo wt[w] para almacenar los pesos de los elementos, ahora se usa para almacenar la mínima distancia al árbol, si el vértice w aún no pertenece al árbol; y la distancia al padre, si el vértice w ya pertenece al MST. Al inicio se lo llena con maxWT, para indicar que esta información sobre pesos mínimos aún no se conoce. En la variable local min se almacena el vértice, que aún no pertenece al MST, y el cual debe cumplir la propiedad de tener distancia mínima con los vértices que ya están en el MST. Para asociarle un peso, se agrega una entrada al arreglo wt, con valor maxWT, y se lo inicia con valor V (vértice que no existe en el grafo). De esta forma wt[min] tendrá un espacio y valor definido. MST w
  • 23. Grafos 23 Profesor Leopoldo Silva Bijit 20-01-2010 #define NoVisitado -1 #define SinPadre -1 #define Peso G->adj[v][w] void mstPrim(Graph G, int raiz) { //Inicio del espacio de variables int v, w, min=raiz, cnt; for (v = 0; v < G->V; v++) { padre[v] = SinPadre; DistaMenosDe[v] = v; wt[v] = maxWT; } wt[G->V] = maxWT; //centinela. Arreglo requiere una posición adicional wt[raiz]=0.; //raíz a distancia cero de sí misma. for (cnt=0; cnt< G->V; cnt++) //agrega todos los vertices O(V2) { v = min; padre[v] = DistaMenosDe[v]; //agrega vértice v al MST //printf(" %d n", v); //orden en que son agregados los vértices. //Selecciona vértice min con distancia mínima a los vértices del mst for (w = 0, min = G->V; w < G->V; w++) //Al inicio min es un vértice que no existe. if (padre[w] == NoVisitado) //si w no está aún en el MST { if (Peso < wt[w]) { wt[w] = Peso; DistaMenosDe[w] = v; } //salva distancia menor y el vértice. if (wt[w] < wt[min]) min = w; //selecciona nuevo vértice a distancia mínima } //Al salir del for interno se tiene el nuevo vértice que se agrega al MST } } Para los datos anteriores, luego de ejecutado el Algoritmo de Prim, con raíz en vértice 0, se genera: 0 1 2 3 4 5 6 7 Vértices. 0 7 0 4 7 3 7 0 Padre del vértice. 0 0.21 0.29 0.34 0.46 0.18 0.25 0.31 Peso elemento entre vértice y su Padre. 0 7 0 4 7 3 7 0 Vértice que dista menos del vértice del árbol La suma = 0.21+0.29+0.34+0.46+0.18+0.25+0.31 es la mínima. 10.5.2. Algoritmo de Kruskal. Se inicia con un conjunto vacío los vértices del árbol de mínima cobertura. Luego de ordenados los elementos por sus pesos, se agrega un elemento por vez, los de menor peso se intentan agregar primero. Antes de agregar los vértices del elemento al árbol, se revisa que no se formen circuitos. El procedimiento termina cuando se tengan todos los vértices en el conjunto o se hayan agregado (Vértices-1) ramas al árbol.
  • 24. 24 Estructuras de Datos y Algoritmos Profesor Leopoldo Silva Bijit 20-01-2010 Verificar que los vértices agregados, asociados a un elemento, no formen circuitos no es una tarea sencilla. Una primera aproximación es definir un arreglo id de vértices que mantenga la información booleana de si un vértice ha sido agregado o no. La Figura 10.15, muestra las ramas, definidas por sus vértices: (1, 4), (1, 5) y (0, 3), que ya han sido agregadas al árbol. Al inicio se marcan todos los vértices con 0, indicando que aún no han sido considerados. Entonces si el and de id[v] e id[w] es verdadero no se agrega el elemento (u,w) y se considera el siguiente elemento de mayor peso; si es falso se marcan los vértices con 1, indicando su consideración. Los vértices: 2 y 6 aún no han sido agregados, y los elementos considerados forman una foresta o un conjunto de subárboles no conectados entre sí. 1 4 5 0 3 0 1 2 3 4 5 6 1 1 0 1 1 1 0 Figura 10.15. Generación MST. Algoritmo de Kruskal. La información anterior no permitiría agregar una rama que conecte, por ejemplo los vértices 3 y 4; ya que estos vértices ya están marcados. Para superar esto pueden identificarse en el arreglo a cual de los subárboles pertenecen los vértices. Se inicia el arreglo foresta con el índice correspondiente al vértice; de este modo, al inicio cada vértice queda asociado a un subárbol vacío. Al agregar un elemento cuyos dos vértices no pertenezcan a ninguno de los subárboles se los marca pertenecientes a un árbol de la foresta; se emplea números para identificar los árboles con enteros mayores que el número de vértices. Después de agregar el elemento (1, 4) el arreglo foresta queda como se indica en la Figura 10.15a. 1 4 0 1 2 3 4 5 6 0 7 2 3 7 5 6 foresta Figura 10.15a. Creación de primer árbol de la foresta. La condición: (foresta[u] != foresta[w]) toma valor verdadero si el elemento (u, w) no pertenece al árbol. Si se agrega elemento (1,5), el vértice 5 no pertenece a un subárbol y el vértice 1 pertenece al subárbol 7, se marca el vértice 5 como perteneciente al subárbol 7. Esto se ilustra en la Figura 10.15b.
  • 25. Grafos 25 Profesor Leopoldo Silva Bijit 20-01-2010 1 4 0 1 2 3 4 5 6 0 7 2 3 7 7 6 foresta 5 Figura 10.15b. Agrega elemento a subárbol de la foresta. Si se agrega el elemento (0, 3), se crea un nuevo subárbol con identificador 8. Resulta la Figura 10.15c. 1 4 0 1 2 3 4 5 6 8 7 2 8 7 7 6 foresta 5 0 3 Figura 10.15c. Agrega elemento a subárbol de la foresta. Si se desea agregar el elemento (3, 4), con la información de los subárboles de la foresta, ahora es posible efectuarlo. Sin embargo deben unirse los subárboles, esto puede efectuarse cambiado los identificadores de uno de los subárboles. Esto se muestra en la Figura 10.15d. 1 4 0 1 2 3 4 5 6 7 7 2 7 7 7 6 foresta 5 0 3 Figura 10.15d. Unión de subárboles de la foresta. int foresta[VERTICES]; static int cntf; void UneSubArboles(int p, int q) { int i, t; if ( (foresta[p]==p) && (foresta[q]==q) ) {foresta[p]=cntf; foresta[q]=cntf; cntf++;} //crea subárbol else if (foresta[p]<VERTICES) foresta[p]=foresta[q]; //pega p a subárbol q else if(foresta[q]<VERTICES) foresta[q]=foresta[p]; //pega q a subárbol p else for(t=foresta[p], i=0; i<VERTICES; i++) // Complejidad O(VERTICES) if(foresta[i] == t) foresta[i] = foresta[q]; //une subárbol p al subárbol q
  • 26. 26 Estructuras de Datos y Algoritmos Profesor Leopoldo Silva Bijit 20-01-2010 } int NoPertenece(int p, int q) { return (foresta[p] != foresta[q]); } void mstKruskal(Graph G, Edge *e) { int i, k, E; E=GRAPHedges(e, G); qsortnw(e, 0, E-1); //ordena elementos por pesos. for(i=0; i<G->V; i++) foresta[i]=i; //crea conjunto de vértices. cntf=G->V; for (i= 0, k = 0; i < E && k < G->V-1; i++) if (NoPertenece( e[i].v, e[i].w )) //Elemento (u,w) no pertenece al mst { UneSubArboles(e[i].v, e[i].w); //La unión es O(n) mst[k++] = e[i]; //agrega rama al árbol } } En el arreglo mst de elementos, se deja el árbol, se emplea como variable global. Edge mst[VERTICES-1]; La rutina de ordenamiento quicksort, se modifica, para adecuarla a los tipos de datos del grafo, y para ordenar según el peso: void qsortnw(Edge *a, int l, int r) { int i=l, j=r; Edge temp; float piv=a[(l+r)/2].wt; do { while ( a[i].wt < piv) i++; while( piv < a[j].wt) j--; if( i<=j) { if (i!=j) {temp=a[i], a[i]= a[j], a[j]=temp;} i++; j--; } } while(i<=j); if( l < j) qsortnw( a, l, j); if( i < r ) qsortnw( a, i, r); } La siguiente secuencia prueba la función, e imprime el mínimo árbol de cobertura:
  • 27. Grafos 27 Profesor Leopoldo Silva Bijit 20-01-2010 mstKruskal(Grafo, Elementos); for (i = 0; i < Grafo->V-1; i++) printf("%2d-%2d =%0.2fn", mst[i].v, mst[i]. w, mst[i].wt); Las funciones que manejan conjuntos, se encuentra en las primeras páginas del texto de R. Sedgewick. void GRAPHmstKruskal(Graph G, Edge *e) { int i, k, E; E=GRAPHedges(e, G); //Se crea representación de elementos a partir del grafo. qsortnw(e, 0, E-1); //se ordena a los elementos, según el peso. UFinit(G->V); //crea conjunto de vértices. for (i= 0, k = 0; i < E && k < G->V-1; i++) //se agregan máximo V-1 ramas if (!UFfind(e[i].v, e[i].w)) //si v no está conectado a w { UFunion(e[i].v, e[i].w); //se agregan vértices al conjunto mst[k++] = e[i]; //se agrega rama e al árbol mst } UFdestroy(); } En el arreglo id, se identifican los vértices de un grafo Se lleva la cuenta del número de nodos de cada subconjunto en sz. static int *id, *sz; void UFinit(int N) { int i; id = (int *) malloc(N*sizeof(int)); sz = (int *) malloc(N*sizeof(int)); for (i = 0; i < N; i++) { id[i] = i; sz[i] = 1; } } Figura 10.16. Descripción de conjuntos, mediante arreglos. Cada vértice apunta a otro en el mismo subconjunto, sin ciclos. Los vértices conectados, de un subconjunto apuntan a la raíz. int find(int x) { int i = x; while (i != id[i]) i = id[i]; return i; } 0 1 N-1 1 1 1 id sz
  • 28. 28 Estructuras de Datos y Algoritmos Profesor Leopoldo Silva Bijit 20-01-2010 //Retorna uno si están conectados int UFfind(int p, int q) { return (find(p) == find(q)); } void UFunion(int p, int q) // O(2*log(N)) R.S. pág 16 { int i = find(p), j = find(q); if (i == j) return; if (sz[i] < sz[j]) //si nodos conectados a i es menor nodos conectados a j { id[i] = j; sz[j] += sz[i]; } //el i se pega al j y nodos de i se acumulan en j else { id[j] = i; sz[i] += sz[j]; } //el j se pega al i y nodos de j se acumulan en i //for(i=0;i<VERTICES;i++) printf("%d ",id[i]); //for(i=0;i<VERTICES;i++) printf("%d ",sz[i]); //putchar('n'); } void UFdestroy(void) { free(id); free(sz); } Un diseño menos eficiente de las rutinas: //Retorna uno si p y q ya están conectados int UFfind1(int p, int q) { return (id[p] == id[q]); } // O(1) //Cada vértice apunta a otro en el mismo subconjunto, sin ciclos. //Los vértices conectados, de un subconjunto apuntan a la raíz del subárbol void UFunion1(int p, int q, int N) { int t,i; for(t=id[p], i=0; i<N; i++) // O(N) if(id[i] == t) id[i] = id[q]; //le coloca raíz q a todos los conectados al conjunto p } 10.5. Trayectorias más cortas en grafos orientados. La trayectoria más corta es la suma menor de las ponderaciones o pesos de los elementos, a través de una trayectoria orientada. Se denominan redes a los grafos orientados con pesos para sus elementos. Es preciso modificar las rutinas anteriores para tratar grafos dirigidos con pesos. Se considera ahora con peso cero a los elementos de la diagonal principal, reflejando que existe conexión del vértice con sí mismo. No se aceptan elementos en paralelo, ni elementos con pesos negativos.
  • 29. Grafos 29 Profesor Leopoldo Silva Bijit 20-01-2010 Existen tres tipos de problemas. Uno de ellos es: dados dos vértices encontrar la trayectoria orientada más corta entre los vértices, se lo denomina fuente-sumidero. Otro caso: es encontrar todos los trayectos más cortos entre un vértice y todos los demás; esto equivale a encontrar un árbol (SPT shortest-path-tree) que conecte al vértice con todos los demás vértices que son alcanzables, de tal modo que la trayectoria en el árbol sea la más corta dentro de la red. Un tercer problema es encontrar todos los pares de rutas más cortas. 10.5.1. Modificación de las funciones para tratar grafos orientados con pesos. La definición del elemento debe entenderse como dirigido de v hacia w. Edge EDGE(int v, int w, float wt) { Edge t; t.v=v; t.w=w; t.wt=wt; return (t); } v w wt Figura 10.17. Elemento orientado, con peso. Ahora se coloca en la matriz que cada vértice está a distancia cero consigo mismo. //Asignación dinámica de arreglo bidimensional float **MATRIXfloat(int r, int c, float wt) { int i, j; float **t = malloc(r * sizeof(float *)); for (i = 0; i < r; i++) t[i] = malloc(c * sizeof(float)); for (i = 0; i < r; i++) for (j = 0; j < c; j++) if(i==j) t[i][j] = 0.; else t[i][j] = wt; //se aceptan lazos en cada vértice return t; } void BorreGrafo(Graph G) { int i; float **t = G->adj; for (i = 0; i < G->V; i++) free(t[i]); free(t); free(G); } Graph GRAPHinit(int V) //Crea grafo vacío, sin elementos. Sólo los vértices. { Graph G = malloc(sizeof *G); //crea cabecera del grafo G->V = V; G->E = 0; G->adj = MATRIXfloat(V, V, maxWT);
  • 30. 30 Estructuras de Datos y Algoritmos Profesor Leopoldo Silva Bijit 20-01-2010 return G; } void GRAPHinsertE(Graph G, Edge e) //Inserta elemento { if (G->adj[e.v][e.w] == maxWT) G->E++; G->adj[e.v][e.w] = e.wt; // Sólo el elemento dirigido. } void GRAPHremoveE(Graph G, Edge e) //Remueve elemento { if (G->adj[e.v][e.w] != maxWT) G->E--; G->adj[e.v][e.w] = maxWT; } int GRAPHedges(Edge a[], Graph G) //Crea arreglo a de elementos a partir de G. { int v, w, E = 0; for (v = 0; v < G->V; v++) for (w = 0; w < G->V; w++) if ((G->adj[v][w] != maxWT)&&(G->adj[v][w] != 0)) a[E++] = EDGE(v, w, G->adj[v][w]); return E; } //muestra la matriz mediante listas de vértices conectados a cada vértice void GRAPHshowL(Graph G) { int i, j; printf("%d vértices, %d elementos.n", G->V, G->E); for (i = 0; i < G->V; i++) { printf("%d: ", i); for (j = 0; j < G->V; j++) if (G->adj[i][j] != maxWT) printf(" %2d-%0.2f", j, G->adj[i][j]);//dos decimales putchar('n'); } } //Muestra Matriz de incidencia. void GRAPHshowM(Graph G) { int i, j; printf("%d vértices, %d elementos.n", G->V, G->E); printf(" "); for (j = 0; j < G->V; j++) printf(" %4d ", j); printf("n"); for (i = 0; i < G->V; i++) { printf("%2d:", i); for (j = 0; j < G->V; j++) if (G->adj[i][j] != maxWT) printf(" %0.2f", G->adj[i][j]); else printf(" * "); putchar('n'); }
  • 31. Grafos 31 Profesor Leopoldo Silva Bijit 20-01-2010 } Las siguientes definiciones, permiten crear un grafo de seis vértices y 11 elementos. #define VERTICES 6 #define ELEMENTOS 11 //Variables Graph Grafo; Edge Elementos[ELEMENTOS]={{0,1,.41},{1,2,.51},{2,3,.50},{4,3,.36}, {3,5,.38},{3,0,.45},{0,5,.29},{5,4,.21}, {1,4,.32},{4,2,.32},{5,1,.29} }; Se inicia el grafo con: Grafo = GRAPHinit(VERTICES); Y la inserción de los elementos con sus pesos se logra con: for(i=0; i<ELEMENTOS; i++) GRAPHinsertE(Grafo, Elementos[i] ); La lista de los vértices conectados a cada vértice se logra con: GRAPHshowL(Grafo); La cual muestra: 0: 0-0.00 1-0.41 5-0.29 1: 1-0.00 2-0.51 4-0.32 2: 2-0.00 3-0.50 3: 0-0.45 3-0.00 5-0.38 4: 2-0.32 3-0.36 4-0.00 5: 1-0.29 4-0.21 5-0.00 La matriz de la red, se logra con: GRAPHshowM(Grafo); Que muestra: 6 vértices, 11 elementos. 0 1 2 3 4 5 0: 0.00 0.41 * * * 0.29 1: * 0.00 0.51 * 0.32 * 2: * * 0.00 0.50 * * 3: 0.45 * * 0.00 * 0.38 4: * * 0.32 0.36 0.00 * 5: * 0.29 * * 0.21 0.00 La Figura 10.18 muestra el grafo.
  • 32. 32 Estructuras de Datos y Algoritmos Profesor Leopoldo Silva Bijit 20-01-2010 0 0.41 1 5 0.29 2 0.51 4 0.32 3 0.50 0.45 0.38 0.32 0.36 0.29 0.21 Figura 10.18. Grafo orientado. Se puede crear un árbol de trayectorias, entre un vértice y el resto, a partir del grafo G, con una búsqueda primero en profundidad, invocando a: GRAPHsearchDFS(Grafo); static int cnt; static int pre[VERTICES]; static int padre[VERTICES]; static float wt[VERTICES]; //distancia menor de raíz a vértice #define Pesoew G->adj[e.v][e.w] void dfsR(Graph G, Edge e) { int t; pre[e.w] = cnt++; //orden en que recorre el árbol padre[e.w] = e.v; //se guarda el vértice v padre de w. wt[e.w] = wt[e.v] + Pesoew; //se guarda el peso del elemento v-w más el acumulado // desde la raíz a v. for (t = 0; t < G->V; t++) if (G->adj[e.w][t] != maxWT) if (pre[t] == NoVisitado) {dfsR(G, EDGE(e.w, t, maxWT)); /*printf("%d-%d n", e.w, t);*/ } } La rutina recursiva anterior es llamada por: void GRAPHsearchDFS(Graph G) { int v; cnt = 0; for (v = 0; v < G->V; v++) {pre[v] = NoVisitado; padre[v] = SinPadre; wt[v]=0.;} for (v = 0; v < G->V; v++) if (pre[v] == NoVisitado) dfsR(G, EDGE(v, v, maxWT)); //Si el grafo no es conectado, obtiene foresta. } La cual genera el siguiente árbol de trayectorias, descrito por un arreglo de padres.
  • 33. Grafos 33 Profesor Leopoldo Silva Bijit 20-01-2010 0 1 2 3 4 5 Vértices. 0 0 1 2 5 3 Padre del vértice. 0.00 0.41 0.92 1.42 2.01 1.80 Peso trayecto entre vértice y la raíz. 0 1 2 3 5 4 Orden en que visita los vértices. El árbol DFS generado no es un SPT mínimo. 0 0.41 1 5 2 0.51 4 3 0.50 0.38 0.21 0 0.41 1 5 0.29 2 0.51 4 0.32 3 0.50 0.45 0.38 0.32 0.36 0.29 0.21 Figura 10.19. Árbol de trayectorias DFS. El árbol de búsqueda primero en extensión es una mejor aproximación, pero tampoco es un SPT. 0 1 2 3 4 5 Vértices. 0 0 1 2 1 0 Padre del vértice. 0.00 0.41 0.92 1.42 0.73 0.29 Peso trayecto entre vértice y la raíz. 0 1 3 5 4 2 Orden en que visita los vértices. 0 0.41 1 5 0.29 2 0.51 4 0.32 3 0.50 0 0.41 1 5 0.29 2 0.51 4 0.32 3 0.50 0.45 0.38 0.32 0.36 0.29 0.21 Figura 10.19a. Árbol de trayectorias BFS. 10.5.2. Algoritmo de Dijkstra. Determina un SPT, un árbol con las mínimas trayectorias, desde un vértice a los demás. No se aceptan elementos en paralelo, ni elementos con pesos negativos. El algoritmo consiste inicialmente en colocar el vértice fuente en el SPT. Luego, se agrega un elemento por vez, agregando un vértice. Siempre tomando el elemento que tenga el trayecto más corto entre la fuente y el vértice que no está en el SPT.
  • 34. 34 Estructuras de Datos y Algoritmos Profesor Leopoldo Silva Bijit 20-01-2010 Se implementa una solución similar al algoritmo de Prim, pero se van agregando vértices que estén a la menor distancia de la raíz. Escoger raíz. Repetir hasta agregar todos los vértices: Encontrar un nodo (sea min) cuya distancia a la raíz sea la menor entre todos los nodos no pertenecientes al SPT. Marcar ese nodo (sea v) como perteneciente al SPT. Repetir para cada w nodo no perteneciente al SPT: Si hay conexión entre v y w, y si la distancia del nodo raíz a v más la distancia de v a w es menor que la distancia actual de la raíz a w: Actualizar la distancia de la raíz a w como la distancia de la raíz a v más la distancia de v a w. Actualizar el nuevo padre de w Actualizar el vértice que está a distancia mínima. raíz v w wt[w] wt[v] Figura 10.20. Distancias en algoritmo de Dijkstra. Se requieren tres arreglos para implementar el algortimo: static int padre[VERTICES]; static int DistaMenosDe[VERTICES]; //vértice que dista menos del vértice del árbol. static float wt[VERTICES+1]; //distancia menor de raíz a vértice. Requiere un espacio // adicional que se emplea como centinela. #define Peso G->adj[v][w] #define PesoRuta wt[v] + Peso //distancia de raíz a v, más la del elemento v-w #define SinPadre -1 #define NoVisitado -1 void sptDijkstra(Graph G, int raiz) { //Inicio del espacio de variables int v, w, min=raiz, cnt; for (v = 0; v < G->V; v++)
  • 35. Grafos 35 Profesor Leopoldo Silva Bijit 20-01-2010 { padre[v] = SinPadre; //se inician como no visitados todos los vértices DistaMenosDe[v] = v; //Al inicio cada vértice dista menos de si mismo. wt[v] = VERTICES ; //Peso máximo de los trayectos = número de ramas + 1 } wt[raiz] = 0.; //raíz a distancia cero wt[G->V] = VERTICES;//centinela. Requiere una posición adicional con peso máximo. //El for externo agrega, uno a uno, los vértices al SPT. for (cnt=0; cnt< G->V; cnt++) //agrega todos los vértices. O(V2) { v = min; padre[min] = DistaMenosDe[min]; //agrega vértice v al SPT //printf(" %d n", min);//orden en que agrega los vértices for (w = 0, min = G->V; w < G->V; w++) //Al inicio min es un vértice que no existe. if (padre[w] == NoVisitado) //si w no está en el SPT { if ( (Peso!=maxWT) && (PesoRuta < wt[w]) ) //Si hay conexión desde v a w, y //Si la distancia del nodo raíz a v más la distancia de v a w es menor que // la distancia actual de la raíz a w { wt[w] = PesoRuta; //actualiza distancia de la raíz a w DistaMenosDe[w] = v; //salva al padre de w. } if (wt[w] < wt[min]) min = w; //actualiza el vértice candidato al mínimo } //Al salir del for interno se tiene el nuevo vértice que se agrega al SPT } } El siguiente llamado genera un shortest path tree, como un arreglo de padres, a partir del grafo G, con fuente o raíz 2. sptDijkstra(Grafo, 2); 0 1 2 3 4 5 Vértices. 3 5 2 2 5 3 Padre del vértice. 0.95 1.17 0.00 0.50 1.09 0.88 Peso trayecto entre vértice y raíz. wt[i] 3 5 2 2 5 3 Vértice que Dista Menos Del vértice del árbol La raíz 2 a distancia cero de sí misma.
  • 36. 36 Estructuras de Datos y Algoritmos Profesor Leopoldo Silva Bijit 20-01-2010 0 1 5 2 4 3 0.50 0.45 0.38 0.29 0.21 Figura 10.21. SPT, raíz 2. El SPT con fuente en el vértice 0 se genera con: sptDijkstra(Grafo, 0). 0 1 2 3 4 5 Vértices. 0 0 4 4 5 0 Padre del vértice. 0.00 0.41 0.82 0.86 0.50 0.29 Peso trayecto entre vértice y raíz. 0 0 4 4 5 0 Vértice que dista menos del vértice del árbol 0 0.41 1 5 0.29 2 0.51 4 0.32 3 0.50 0.45 0.38 0.32 0.36 0.29 0.21 Figura 10.22. SPT, raíz 0. El SPT con fuente en el vértice 1 se genera con: sptDijkstra(Grafo, 1); 0 1 2 3 4 5 Vértices. 3 1 1 4 1 3 Padre del vértice. 1.13 0.00 0.51 0.68 0.32 1.06 Peso trayecto entre vértice y raíz. sptDijkstra(Grafo, 5), produce: 0 1 2 3 4 5 Vértices. 3 5 4 4 5 5 Padre del vértice. 1.02 0.29 0.53 0.57 0.21 0.00 Peso trayecto entre vértice y raíz. sptDijkstra(Grafo, 4), produce: 0 1 2 3 4 5 Vértices. 3 5 4 4 4 3 Padre del vértice. 0.81 1.03 0.32 0.36 0.00 0.74 Peso trayecto entre vértice y raíz.
  • 37. Grafos 37 Profesor Leopoldo Silva Bijit 20-01-2010 Referencias. Robert Sedgewick, "Algorithms in C", Third edition, Addison Wesley, 1998.
  • 38. 38 Estructuras de Datos y Algoritmos Profesor Leopoldo Silva Bijit 20-01-2010 Problemas resueltos. P10.1. Para el siguiente grafo orientado: Determinar el arreglo de pesos del trayecto de peso mínimo entre cada vértice y la raíz, y el arreglo padres del vértice. a) Para raíz igual al vértice 4. b) Para raíz igual al vértice 2. Solución: a) 0 1 2 3 4 Vértices. 4 0 3 4 4 Padre del vértice. 0.30 0.70 1.00 0.50 0.00 Peso trayecto entre vértice y raíz. b) 0 1 2 3 4 Vértices. 4 2 2 1 1 Padre del vértice. 0.90 0.50 0.00 0.80 0.60 Peso trayecto entre vértice y raíz. P10.2. Se tiene un grafo definido por un arreglo de elementos. Edge Elementos[ELEMENTOS]={{0,1,.4},{1,2,.5},{2,3,.5},{3,4,.5}, {1,4,.1},{0,4,.3},{1,3,.3}}; a) Dibujar el grafo. b) Determinar el mínimo árbol de cobertura aplicando algoritmo de Kruskal. Indicando el orden en que se eligen las ramas. Dibujar el árbol. c) Determinar el mínimo árbol de cobertura aplicando algoritmo de Prim. Indicando el orden en que se agregan los vértices, y los arreglos de padres y de pesos entre el vértice y su padre. Dibujar el árbol. d) Modificar la función de Prim para imprimir el orden en que se eligen los vértices. Solución. 0 4 0,3 1 0,4 0,1 3 0,3 0,5 0,5 0,5 2
  • 39. Grafos 39 Profesor Leopoldo Silva Bijit 20-01-2010 a) b) Se eligen en el orden: 1- 4 =0.10 1- 3 =0.30 0- 4 =0.30 2- 3 =0.50 c) Se agregan en orden: 0, 4, 1, 3, 2. 0 1 2 3 4 Vértices. 0 4 1 1 0 Padre del vértice. 1.00 0.10 0.50 0.30 0.30 Peso elemento entre vértice y su Padre. d) Se intercala el printf, entre las líneas que se indican. ……….. v = min; st[min] = fr[min];//agrega vértice v al MST printf(" %d n", min); for (w = 0, min = G->V; w < G->V; w++) ………. Ejercicios propuestos. E10.1. Partiendo del nodo A, determinar arreglo de padres para: Árbol de búsqueda primero en extensión (bfs) Árbol de búsqueda primero en profundidad (dfs) Árbol de cobertura mínima aplicando algoritmo de Prim. 0 4 0,3 1 0,4 0,1 3 0,3 0,5 0,5 0,5 2 0 0,3 4 1 0,1 3 0,3 0,5 2 0 0,3 4 1 0,1 3 0,3 0,5 2
  • 40. 40 Estructuras de Datos y Algoritmos Profesor Leopoldo Silva Bijit 20-01-2010 A B C D E F G H I J K 4 7 22 14 8 10 4 13 6 18 17 22 5 13 3 9 8
  • 41. Grafos 41 Profesor Leopoldo Silva Bijit 20-01-2010 Índice general. CAPÍTULO 10 ........................................................................................................................................... 1 GRAFOS. .................................................................................................................................................... 1 10.1. DEFINICIONES. ................................................................................................................................. 1 10.2. REPRESENTACIONES. ....................................................................................................................... 2 10.2.1. Matriz de adyacencia de los elementos en los vértices. .......................................................... 2 10.2.2. Matrices estáticas en C. .......................................................................................................... 3 10.2.3. Matrices dinámicas en C. ........................................................................................................ 4 Declaración de estructuras de datos para un grafo. .......................................................................................... 4 Definición de variables. ................................................................................................................................... 4 10.2.4. Funciones para grafos descritos por su matriz de adyacencias. ............................................. 5 10.2.4.1. Creación. ........................................................................................................................................... 5 10.2.4.2. Liberación del espacio. ..................................................................................................................... 6 10.2.4.3. Definición de un elemento. ............................................................................................................... 6 10.2.4.4. Inserción de elemento en un grafo. ................................................................................................... 6 10.2.4.5. Eliminación de elemento. .................................................................................................................. 7 10.2.4.6. Creación de los elementos. ................................................................................................................ 7 10.2.4.7. Despliegue de un grafo. Lista de vértices. ......................................................................................... 8 10.2.4.8. Despliegue de la matriz de incidencias. ............................................................................................ 9 10.2.4.9. Generación de los elementos a partir de la matriz de incidencias. .................................................... 9 10.3. TRAYECTORIAS EN GRAFOS. .......................................................................................................... 10 10.3.1. Búsqueda primero en profundidad. ....................................................................................... 11 10.3.1.1. Definición de búsqueda primero en profundidad. ........................................................................... 11 10.3.1.2. Diseño de la operación. ................................................................................................................... 12 10.3.1.3. Modificación para entender la operación. ....................................................................................... 12 10.3.1.4. Arreglo de padres. ........................................................................................................................... 13 10.3.1.5. Uso de stack en búsqueda en profundidad. ..................................................................................... 14 10.3.2. Búsqueda primero en extensión. ............................................................................................ 15 10.3.2.1. Definición de la búsqueda primero en extensión............................................................................. 15 10.3.2.2. Diseño de la operación. ................................................................................................................... 16 10.4. ÁRBOLES CON PESO. ...................................................................................................................... 17 Modificación de las funciones para tratar grafos con pesos............................................................. 17 10.5. MÍNIMO ÁRBOL DE COBERTURA. .................................................................................................... 21 10.5.1. Algoritmo de Prim. ................................................................................................................ 22 10.5.2. Algoritmo de Kruskal. ........................................................................................................... 23 10.5. TRAYECTORIAS MÁS CORTAS EN GRAFOS ORIENTADOS. ................................................................ 28 10.5.1. Modificación de las funciones para tratar grafos orientados con pesos. .............................. 29 10.5.2. Algoritmo de Dijkstra. ........................................................................................................... 33 REFERENCIAS. ........................................................................................................................................ 37 PROBLEMAS RESUELTOS. ........................................................................................................................ 38 P10.1. Para el siguiente grafo orientado: ......................................................................................... 38 P10.2. Se tiene un grafo definido por un arreglo de elementos. ....................................................... 38 Ejercicios propuestos. ....................................................................................................................... 39 E10.1. ................................................................................................................................................ 39 ÍNDICE GENERAL. ................................................................................................................................... 41
  • 42. 42 Estructuras de Datos y Algoritmos Profesor Leopoldo Silva Bijit 20-01-2010 ÍNDICE DE FIGURAS. ................................................................................................................................ 43
  • 43. Grafos 43 Profesor Leopoldo Silva Bijit 20-01-2010 Índice de figuras. FIGURA 10.1. ELEMENTO, VÉRTICE, INCIDENCIA........................................................................................... 1 FIGURA 10.2. ELEMENTOS ORIENTADOS. ...................................................................................................... 2 FIGURA 10.3. MATRIZ DE ADYACENCIAS. ..................................................................................................... 3 FIGURA 10.4. MATRIZ DE 2 RENGLONES POR 4 COLUMNAS. .......................................................................... 3 FIGURA 10.5. FORMA DE ALMACENAMIENTO DE MATRICES. ......................................................................... 3 FIGURA 10.6. ESTRUCTURA PARA MATRIZ DE ADYACENCIAS. ...................................................................... 5 FIGURA 10.7. GRAFO DESCRITO POR SUS ELEMENTOS. .................................................................................. 7 FIGURA 10.8. GRAFO PARA ELEMENTOS DE LA FIGURA 10.7. ........................................................................ 8 FIGURA 10.9. GENERACIÓN DE LOS ELEMENTOS A PARTIR DE MATRIZ DE ADYACENCIAS. .......................... 10 FIGURA 10.10. ORDEN DE VISITAS, GENERACIÓN DE LLAMADOS. ............................................................... 11 FIGURA 10.11. VISUALIZACIÓN DE LOS LLAMADOS, EN PROFUNDIDAD....................................................... 14 FIGURA 10.12. EJEMPLO PARA BÚSQUEDA PRIMERO EN EXTENSIÓN. .......................................................... 15 FIGURA 10.13. ORDEN DE VISITAS, GENERACIÓN DE LLAMADOS. EN EXTENSIÓN. ...................................... 16 FIGURA 10.14. CONJUNTOS EN EL ALGORITMO DE PRIM. ............................................................................ 22 FIGURA 10.15. GENERACIÓN MST. ALGORITMO DE KRUSKAL. .................................................................. 24 FIGURA 10.15A. CREACIÓN DE PRIMER ÁRBOL DE LA FORESTA. ................................................................. 24 FIGURA 10.15B. AGREGA ELEMENTO A SUBÁRBOL DE LA FORESTA. ........................................................... 25 FIGURA 10.15C. AGREGA ELEMENTO A SUBÁRBOL DE LA FORESTA. ........................................................... 25 FIGURA 10.15D. UNIÓN DE SUBÁRBOLES DE LA FORESTA. .......................................................................... 25 FIGURA 10.16. DESCRIPCIÓN DE CONJUNTOS, MEDIANTE ARREGLOS. ......................................................... 27 FIGURA 10.17. ELEMENTO ORIENTADO, CON PESO. ..................................................................................... 29 FIGURA 10.18. GRAFO ORIENTADO. ............................................................................................................ 32 FIGURA 10.19. ÁRBOL DE TRAYECTORIAS DFS. ......................................................................................... 33 FIGURA 10.19A. ÁRBOL DE TRAYECTORIAS BFS. ....................................................................................... 33 FIGURA 10.20. DISTANCIAS EN ALGORITMO DE DIJKSTRA. ......................................................................... 34 FIGURA 10.21. SPT, RAÍZ 2. ........................................................................................................................ 36 FIGURA 10.22. SPT, RAÍZ 0. ........................................................................................................................ 36