Está en la página 1de 13

UNIVERSIDAD TECNOLOGICA DE PANAMÁ

CENTRO REGIONAL DE AZUERO

FACULTAD DE INGENERÍA DE SISTEMA COMPUTACIONALES

NOMBRE DEL PROFESOR:

JOSE RUIZ

NOMBRE DE LOS ESTUDIANTES:

BRIAN CANTO / 6-725-931

CARLOS CORDOBA / 7-713-2021

ELIAN GONZALES / 6-725-479

SERGIO GOMEZ / 6-722-67

TALLER DE GRAFOS

FECHA DE ENTREGA

VIERNES 13 DE MAYO DE 2022


Definición y conceptos
Los grafos son una composición interesante de conjuntos de objetos que denominamos
nodos. En ellos se almacena diferentes tipos de elementos o datos que podemos utilizar para
procesar o conocer con fines específicos.
Adicionalmente estos nodos, suelen estar unidos o conectados a otros nodos a través de
elementos que denominamos aristas.
Los nodos pertenecientes a un grafo pueden contener datos estructurada o no estructurada y
al interrelacionarse con otros nodos producen relaciones interesantes que podemos analizar
con diferentes finalidades.
Estos elementos son reconocidos por su capacidad de manejar altos volúmenes de datos y
ser fácilmente procesados por motores de búsqueda o gestores de bases de datos orientados
a grafos.

La teoría de grafos es una mezcla impresionante de cultura, historia, soluciones matemáticas


y retos que llevaron un buen tiempo para ser resueltos con fórmulas matemáticas.

La matriz de adyacencia de un grafo es simétrica. Si un vértice es aislado entonces la


correspondiente fila (columna) está compuesta sólo por ceros. Si el grafo es simple entonces
la matriz de adyacencia contiene solo ceros y unos (matriz binaria) y la diagonal está
compuesta sólo por ceros.

En las matrices de camino, los elementos se distribuyen uniformemente a lo largo de un


camino o una parte de la trayectoria. Un camino puede ser una línea, una polilínea, una
polilínea 3D, una spline, una hélice, un arco, un círculo o una elipse.
Representación enlazada de grafos

Los grafos se representan en memoria enlazada mediante listas de adyacencia.


combina las matrices de adyacencia con las listas de aristas. Para cada vértice iii,
almacena un arreglo de los vértices adyacentes a él. Típicamente tenemos un arreglo
de |V|∣V∣ vertical bar, V, vertical bar listas de adyacencia, una lista de adyacencia por
vértice. Aquí está una representación de una lista de adyacencia del grafo de la red
social.

En JavaScript, representamos estas listas de adyacencia como:

Los números del vértice en una lista de adyacencia no están obligados a aparecer en
ningún orden en particular, aunque a menudo es conveniente enumerarlos en orden
ascendente, como en este ejemplo.

Algoritmo de Warshall-Dijkstra

El algoritmo de Dijkstra, también llamado algoritmo de caminos mínimos, es


un algoritmo para la determinación del camino más corto, dado un vértice origen,
hacia el resto de los vértices en un grafo que tiene pesos en cada arista. Su nombre
alude a Edsger Dijkstra, científico de la computación de los Países Bajos que lo
concibió en 1956 y lo publicó por primera vez en 1959.

La idea subyacente en este algoritmo consiste en ir explorando todos los caminos


más cortos que parten del vértice origen y que llevan a todos los demás vértices;
cuando se obtiene el camino más corto desde el vértice origen hasta el resto de los
vértices que componen el grafo, el algoritmo se detiene. Se trata de una
especialización de la búsqueda de costo uniforme y, como tal, no funciona en grafos
con aristas de coste negativo (al elegir siempre el nodo con distancia menor, pueden
quedar excluidos de la búsqueda nodos que en próximas iteraciones bajarían el costo
general del camino al pasar por una arista con costo negativo).

Una de sus aplicaciones más importantes reside en el campo de la telemática.


Gracias a él, es posible resolver grafos con muchos nodos, lo que sería muy
complicado resolver sin dicho algoritmo, encontrando así las rutas más cortas entre un
origen y todos los destinos en una red.

Pseudocódigo
En el siguiente algoritmo de pseudocódigo, dist es una matriz que contiene las
distancias actuales desde la fuente a otros vértices, es decir, dist [ u ] es la distancia
actual desde la fuente al vértice u . La matriz anterior contiene punteros a los nodos
de salto anterior en la ruta más corta desde la fuente al vértice dado (de manera
equivalente, es el siguiente salto en la ruta desde el vértice dado a la fuente). El
código u ← vértice en Q con min dist [u], busca el vértice u en el conjunto de vértices
Q que tiene el menor valor de dist [ u ] . length ( u , v ) devuelve la longitud del borde
que une (es decir, la distancia entre) los dos nodos vecinos u y v . La variable alt en la
línea 18 es la longitud de la ruta desde el nodo raíz hasta el nodo vecino v si pasara
por u. Si esta ruta es más corta que la ruta más corta actual registrada para v, esa
ruta actual se reemplaza con esta ruta alternativa.

NULO o POSB : = NULO, entonces: Hacer POS : =


NULO.
Si no: Llamar BUSCA (DEST, ENL, ADY[POSA], POSB, POS).
4.-Volver.
Operaciones sobre grafos
Las operaciones básicas sobre grafos son las de comprobación de existencia de
arista entre dos vértices (o conocer su longitud, si el grafo es etiquetado), recorrer la
lista de vértices adyacentes a uno dado, la inserción y borrado de una arista, y la
inserción y borrado (junto con las aristas asociadas) de un vértice. En muchas
aplicaciones, sin embargo, el conjunto de vértices no varía durante la ejecución.
 Búsqueda:
Suponga que queremos encontrar la posición POS de un nodo N de un grafo C.
Esto se puede llevar a cabo mediante el ejemplo siguiente, tal como sigue:

Llamar BUSCA (NODO, SIG, PRINCIPIO, N, POS)

O sea, que esta sentencia de llamada busca en la lista NODO el nodo N.

Ejemplo: Busca(INFO, ENL, PRINCIPIO, ITEM, POS) Busca la posición POS


del primer nodo que contiene a ITEM, o hace POS : = NULO.

1.-Hacer PTR : =PRINCIPIO.


2.-Repetir mientras PTR sea diferente de NULO:
Si ITEM = INFO[PTR], entonces; Hacer POS :=
PTR y Volver.
Si no hacer PTR : = ENL
[PTR]. [Fin del Bucle].
3.-Hacer POS : = NULO y volver.

Por otro lado, suponga que queremos encontrar la posición POS de una arista (A
B) del grafo G. Primero debemos encontrar la posición POSA de A y la posición
POSB de B en la lista NODO. Luego debemos de buscar en la lista de
sucesores de A, que tiene el puntero de lista ADY[POSA], la posición POS de
POSB. Esto se implementa con el ejemplo siguiente, que también comprueba que
A y B sean nodos de G. Observe que POS de la posición de POSB en la lista
ARISTA.

Ejemplo: BUSCARISTA(NODO, SIG, ADY, PRINCIPIO, DEST, ENL, A, B,


POS)
Este procedimiento busca la posición POS de una arista (A,
B) del grafo G, o pone POS : = NULO
1.-Llamar BUSCA(NODO, SIG, PRINCIPIO, A, POSA).
2.-Llamar BUSCA(NODO, SIG, PRINCIPIO, B, POSB).
3.-Si POSA: = NULO o POSB : = NULO, entonces:
Hacer POS : = NULO.
Si no: Llamar BUSCA (DEST, ENL, ADY[POSA], POSB, POS).
4.-Volver.
Inserción
Suponga que se va a insertar un nodo N en el grafo G. Observe que N será
asignado a NODO [NDISP], el primer nodo disponible. Mas aún, como N será un
nodo aislado, se debe hacer ADY[NDISP]: = NULO. El ejemplo mostrado hace
esto mediante una variable lógica INDIC que indica si hay desbordamiento.

Por supuesto, en el ejemplo siguiente debe ser modificado si la lista NODO se


mantiene como una lista ordenada o como un árbol binario de búsqueda.

Procedimiento 1: INSNODO (NODO, SIG, ADY, PRINCIPIO, NDISP, N, INDIC)


Este procedimiento inserta el nodo N en le grafo G.

1.-[¿DESBORDAMIENTO?] Si NDISP= NULO, entonces: Hacer


INDIC := FALSO y volver.
2.- Hacer ADY[NDISP] : = NULO.
3.-[Quitar el nodo de la lista NDISP].
Hacer NUEVO: =NDISP y NDISP :=SIG[NDISP].
4.-[Insertar nodo N en la lista NODO].
Hacer NODO[NUEVO] : = N, SIG[NUEVO] :=PRINCIPIO y
PRINCIPIO : = NUEVO.
5.-Hacer INDIC := VERDADERO y Vol

Suponga que se va a insertar una arista (A, B) en el grafo G (El procedimiento


asumirá que tanto A como B son nodos de G). El procedimiento busca la posición
POSA de A y la posición POSB de B en la lista NODO. Luego se inserta(A, B)
como una arista de G insertando POSB en la lista de sucesores de A, que tiene el
puntero de la lista ADY[POSA]. De nuevo se usa una variable lógica INDIC para
indicar el desbordamiento.

El procedimiento es el siguiente:

Procedimiento 2.- INSARISTA(NODO, SIG, ADY, PRINCIPIO, DEST, ENL,


ADISP, A, B, INDIC)

Este procedimiento inserta la arista (A, B) en el grafo G

1.-Llamar BUSCA(NODO, SIG, PRINCIPIO, A, POSA)


2.- Llamar BUSCA(NODO, SIG, PRINCIPIO, B, POSB)
3.- [¿DESBORDAMIENTO?] Si ADISP = NULO, entonces:
Hacer INDIC := FALSO y Volver.
4.- [Eliminar nodo de la lista ADISP]. Hacer NUEVO: = ADISP y
ADISP ; = ENL[ADISP]
5.-[Insertar POSB en la lista de sucesores de A ].
Hacer DEST[NUEVO]: = POSB,
ENL[NUEVO]: = ADY[POSA] y ADY[POSA]: = NUEVO.
6.-Hacer INDIC : = VERDADERO y Volver.
Eliminación

Suponga que se va a eliminar una arista (A, B) del grafo G. (Nuestro


procedimiento asumirá que A y B son nodos de G). Debemos encontrar
primero la posición POSA de A y la posición POSB de B en la lista de nodos.
Entonces simplemente eliminamos POSB de la lista de sucesores de A, que
tiene el puntero de la lista ADY[POSA]. Se usa una variable INDIC lógica para
indicar si no existe tal arista en el grafo G. El procedimiento es el siguiente:

Procedimiento ELIMARISTA (NODO, SIG, ADY, PRINCIPIO,


DEST, ENL, ADISP, A, B, INDIC).

Este procedimiento elimina la arista (A, B) del grafo G .

1.-Llamar BUSCA (NODO, SIG, PRINCIPIO, A, POSA)


[Localizar
Nodo A].
2.- Llamar BUSCA (NODO, SIG, PRINCIPIO, B, POSB)
[Localizar
Nodo B].
3.-Llamar ELIMINAR (DEST, ENL, ADY[POSA], ADISP,
POSB, INDIC).
4.-Volver.

Si va a eliminar un nodo N del grafo G. Esta operación es más complicada que las
operaciones de búsqueda e inserción y que la de eliminar una arista, ya que
debemos eliminar todas las aristas que contengan a N. Note que esas aristas son
de dos tipos; aquellas que empiezan en N y aquellas que terminan en N. El
procedimiento consistirá fundamentalmente en los siguientes cuatro pasos:
1. - Encontrar la posición POS del nodo N en G.
2. - Eliminar todas las aristas que terminen en N; o sea, eliminar POS de la
lista de sucesores de cada nodo M de G (este paso requiere recorrer la lista
de nodos de G).
3. - Eliminar todas las aristas que empiecen en N. Esto se hace encontrando
la posición COM del primer sucesor y la posición FIN del ultimo sucesor de
N, y luego añadiendo la lista de sucesores de N a la lista ADISP.
4. - Eliminar el nodo N mismo de la lista de NODO.

El procedimiento es el siguiente:

Procedimiento ELIMNODO(NODO, SIG, ADY, PRINCIPIO, NDISP,


DEST, ENL, ADISP, N, INDIC).
Este procedimiento elimina el nodo N del grafo G.
1.- Llamar BUSCA (NODO, SIG, PRINCIPIO, N, POS).
[Localizar el
nodo N ].
2.- Si POS = NULO, entonces : Hacer INDIC ) : = FALSO
y volver. 3.- [Eliminar aristas que terminan en N].
(a).- Hacer PTR : =PRINCIPIO.
(b).- Repetir mientras PTR se a diferente de NULO :
(i).- Llamar ELIMINAR (DEST, ENL, ADY,[PTR],
ADISP, POS, INDIC).
(ii).- Hacer PTR := SIG[PTR].
[Fin del Bucle].
4.- [¿Lista de sucesores vacía?] Si ADY[POS]= NULO,
entonces : Ir al paso 7.
5.-[Encontrar primer y ultimo sucesor de N].
6.-[Añadir lista de sucesores de N a la lista ADISP].
Hacer ENL[FIN] :=ADISP y ADISP := COM.
7.-[Eliminar N ].
Llamar ELIMINAR(NODO, SIG, PRINCIPIO,
NDISP, N, INDIC).
8.- Volver.
Recorridos de un grafo:
La operación de recorrer una estructura de datos consiste en visitar (procesar)
cada uno de los nodos a partir de uno dado. Así, para recorrer un árbol se parte
del nodo raíz y según el orden se visitan todos los nodos. De igual forma, recorrer
un grafo consiste en visitar todos los vértices alcanzables a partir de uno dado.
Hay dos formas de recorrer un grafo: recorrido en profundidad y recorrido en
anchura. Si el conjunto de nodos marcados se trata como una cola, entonces el
recorrido es en anchura; si se trata como una pila, el recorrido es en profundidad.

Recorrido en Anchura:

El recorrido de búsqueda en anchura, en amplitud o expansión, es una estrategia


aplicable indistintamente al caso de grafos dirigidos y no dirigidos. El recorrido en
anchura es una generalización del recorrido por niveles de un árbol.
Se trata de visitar un nodo inicial y luego a todos los nodos que están a un arco de
distancia de éste, luego a todos los nodos que están a dos arcos de distancia de
éste y así sucesivamente, hasta alcanzar a todos los nodos a los que se pueda
llegar desde el nodo inicial. Una aplicación típica de este recorrido es la resolución
de problemas de planificación.
La búsqueda en amplitud se puede utilizar para hallar la distancia más corta entre
algún nodo inicial y los nodos restantes del grafo. Esta distancia más corta es el
mínimo número de aristas que hay que recorrer para pasar desde el nodo inicial
hasta el nodo concreto que se esté examinando.
Comenzando en el nodo “s”, esta distancia se calcula examinando todas las
aristas incidentes en el nodo “s”, y pasando después a un nodo adyacente “w”,
repitiéndose entonces todo el proceso. El recorrido continúa hasta que se hayan
examinado todos los nodos del grafo. En una búsqueda en amplitud, cada nodo se
visita o es procesado en algún sentido, dependiendo de la aplicación concreta. La
búsqueda comienza en un nodo concreto del grafo.
A continuación, la búsqueda se extiende a los nodos del grafo que estén más
próximos al nodo inicial antes de visitar ningún otro. Estos nodos están
relacionados de alguna forma con el nodo especificado, y forman parte de un
grupo que depende de la aplicación concreta. Inicialmente, se visitan todos
aquellos nodos que sean adyacentes al nodo inicial. A continuación, se visitan
todos los nodos que estén a una distancia “2” del nodo inicial. Este proceso se
repite hasta que se haya visitado todos los nodos posibles. Sin embargo, este
enfoque de búsqueda puede dar lugar a problemas. Los nodos ya visitados no
deben visitarse de nuevo. Se puede evitar esta situación marcando aquellos nodos
que se hayan visitado. Si un nodo ya ha sido marcado con anterioridad (esto es, si
ya ha sido alcanzado o visitado), nunca vuelve a ser visitado. Este método utiliza
una cola como estructura auxiliar en la que se mantienen los vértices que se
vayan a procesar posteriormente.
El recorrido en anchura trabaja de la siguiente manera:
 Se visita el nodo inicial.
 Después de visitar el nodo inicial se visitan todos los sucesores.
 Después los sucesores de los sucesores.
 Se repiten los pasos 1 y 2 hasta encontrar un nodo sin arcos salientes o
ya visitados.
 Si después de visitar todos los descendientes del primer nodo, todavía
quedan más nodos por visitar, se repite el proceso reiniciando.

Este tipo de recorrido es muy usado para problemas de planificación (problema de


laberinto).
Al igual que el recorrido en profundidad, en el recorrido en anchura no existe un
único recorrido de grafo, sino un conjunto de ellos.
Para el siguiente grafo, habría varios recorridos en anchura posibles, entre los
cuales podríamos mencionar:
Visitando primero el nodo izquierdo seria:
A→B→D→C→E
Visitando primero el derecho sería:
A→D→B→E→C

Recorrido en Profundidad

Este método es una forma básica de recorrer un grafo implementando


recursividad. En este recorrido se basan los recorridos preorden y postorden para
árboles binarios.
Se puede utilizar una búsqueda en profundidad en un grafo arbitrario para realizar
el recorrido de un grafo general. A medida que se encuentra cada nuevo nodo, se
marca el mismo para evitar volver a visitarlo de nuevo.
La estrategia de recorrido en profundidad es la siguiente:
 Se toma un nodo “s” como comienzo, y se marca.
 A continuación, se toma y se marca un nodo no marcado adyacente a “s”, y
ese nodo pasa a ser el nuevo nodo de partida, dejando posiblemente por el
momento al nodo inicial original con nodos no explorados.
 La búsqueda continúa por el grafo hasta que el camino en curso finalice con
un grafo de salida igual a cero, o bien en un nodo en que todos los nodos
adyacentes estén marcados.
 A continuación, la búsqueda vuelve al último nodo que todavía tenga nodos
adyacentes sin marcar, y continúa marcando todos los nodos de forma
recursiva hasta que ya no queden nodos sin marcar.
Es preciso marcar los nodos en la búsqueda en profundidad. Si no se hiciera esto,
los grafos que contuvieran ciclos darían lugar a bucles infinitos.
Un algoritmo de búsqueda o recorrido en profundidad consta de una única rutina
principal que invoca a un procedimiento recursivo de la manera siguiente:
Principal
 Se marcan todos los nodos del grafo como no visitados.
 Se invoca a recorrido en profundidad (s) para algún nodo inicial “s”.

Procedimiento recorrido en profundidad:


1. Se marca y visita “s”.
2. Para cada vecino “w” de “s”
Si el vecino “w” no está marcado
Se invoca a recorrido en profundidad (w).
El recorrido en profundidad persigue el mismo objetivo que el recorrido en
anchura: visitar todos los vértices del grafo alcanzables desde un vértice dado.
La búsqueda en profundidad empieza por un vértice “V” del grafo “G”; “V” se
marca como visitado.
Después se recorre en profundidad cada vértice adyacente a “V” no visitado; así
hasta que no haya más vértices adyacentes no visitados.
Esta técnica se denomina en profundidad porque la dirección de visitar es hacia
adelante mientras que sea posible; al contrario que la búsqueda en anchura, que
primero visita todos los vértices posibles en amplitud.
La definición recursiva del recorrido en profundidad nos indica que tenemos que
utilizar una “pila” como estructura para guardar los vértices adyacentes no
visitados a uno dado. De igual forma que en el recorrido en anchura, hacemos uso
de una lista de vértices para controlar los ya visitados.
Los usos que pueden darse a este tipo de recorrido son:
 Simular una red, a partir de un grafo y analizar su robustez para
transferencia de información.
 Identificar bucles, los cuales son potenciales causantes de elevación de
tráfico en recorridos.
Hay que tomar en cuenta que, para recorridos primero en profundidad, no existe
un único recorrido de grafo, sino un conjunto de ellos.
Para el siguiente grafo, habría varios recorridos en profundidad posibles, entre los
cuales podríamos mencionar:
Visitando primero el nodo izquierdo seria:
A→D→E→B→C
Visitando primero el nodo derecho seria:
A→B→C→E→D

También podría gustarte