- Arista: relación entre nodos en la que existe un orden (grafo dirigido: -⇒, ⇐-)
 - Arco: relación entre nodos en la que no existe un orden (grafo no dirigido: ---)
 
¿Qué es un grafo?
- Un grafo es un modelo matemático que permite representar relaciones arbitrarias entre objetos.
 

Tipos de grafos

Definición formal


Conceptos básicos
- Bucle: Arco u arista con igual origen que destino.
 - Grado de un nodo: Número de arcos u aristas conectados al nodo.
- Grado de Entrada (gE) de un nodo: Número de arcos o aristas que tienen al nodo como destino
 - Grado de Salida (gS) de un nodo: Número de arcos o aristas que tienen al nodo como origen.
 
 

- V3 es un nodo fuente (nodo que se conecta con todo el mundo)
 - V4 es un nodo sumidero (nodo que no se conecta con nadie pero que es accesible para todo el mundo)
 - V5 es u nodo aislado (ningún grado de entrada ni ninguno de salida)
 

Capacidad de un grafo
- n = número de nodos de un grafo
 

- Número mínimo de arcos en un grafo = 0 (todos aislados)
 - Número máximo de arcos en un grafo = n^2 (todos conectados con todos (con bucles))
 


Representación en memoria
Densidad de un grafo

Clase Grafo (Matriz de adyacencias)

- Hay que almacenar los nodos (de cualquier tipo), las aristas y los pesos
 
| Matrices adyacencias | Listas adyacencias | 
|---|---|
- Usan memoria estática ![]()  | - Usan memoria dinámica | 

Análisis de eficiencia

Listas de adyacencia


Análisis de Eficiencia

Clase Grafo. Métodos básicos
Matriz de adyacencia


- Complejidad 0(1)
 
getNode()

- Complejidad O(n)
 
public int getNode (T node)
{
	for (int i=0; i<size; i++)
		if (nodes[i].equals(node))
			return (i); // returns the node’s position
	return (-1); // search fails, node does not exist
}addNode()

- Complejidad O(n)
 
public void addNode (T node)
{
	// precondition: node does not exits and there is
	// available space for the node.
	if (getNode(node)== -1 && size<nodes.length)
	{
		nodes[size] = node;
		//inserts void edges
		for (int i=0; i<=size; i++)
		{
			edges[size][i]=false;
			edges[i][size]=false;
			weight[size][i]=0;
			weight[i][size]=0;
		}
		++size;
	}
}


removeNode()

- Complejidad O(n)
 
public void removeNode (T node){
	int i = getNode(node);
	
	if (i>=0) {
		--size;
	if (i != size+1) { // it is not the last node
	
		nodes[i] = nodes[size]; //replaces by the last node
		
		//replace elements in the vectors edges and weights
		for (int j=0; j<=size; j++) {
			 edges[j][i]=edges[j][size];
			 edges[i][j]=edges[size][j];
			 weight[i][j]=weight[size][j];
			 weight[j][i]=weight[j][size];
		}
		
		 // loop (diagonal)
		 edges[i][i] = edges[size][size];
		 weight[i][i] = weight[size][size];
	}  
}existsEdge()

- Complejidad O(n)
 
public boolean existsEdge (T origin, T destination)
{
	int i=getNode(origin);
	int j=getNode(destination);
	
	// precondition: both nodes must exist. // if don’t... should we throw an exception?
	
	if (i>=0 && j>=0)
		return(edges[i][j]);
	else
		return (false); 
}addEdge()

- Complejidad O(n)
 
public void addEdge (T origin, T destination, double
edgeWeight)
{
	// precondition: the edge must not already exist.
	if (!existEdge(origin, destination))
	{
		int i=getNode(origin);
		int j=getNode(destination);
		
		edges[i][j]=true;
		
		weight[i][j]=edgeWeight;
	}
	
	else
	; // what about throwing an exception here?
}removeEdge()

- Complejidad O(n)
 
public void removeEdge (T origin, T destination){
	// precondition: the edge must exist.
	if (existsEdge(origin, destination)) {
		int i=getNode(origin);
		int j=getNode(destination);
		
		edges[i][j]=false;
		
		weight[i][j]=0.0;
	}
	
	else
	; // what about throwing an exception?
}print()

- Complejidad O(n^2)
 
public void print(){
 
	for (int k=0; k<size; k++)
		nodes[k].print();
	
	for (int i=0; i<size; i++) {
		for (int j=0; j<size; j++) {
		
		System.out.print(edges[i][j] + “(“);
		
		System.out.print(weight[i][j] + “) “);
	}
	
	System.out.println();
}Más conceptos básicos



Algoritmo de Dijkstra

- Va de un nodo origen al resto
 

Ejemplo

Inicialización

- Dijkstra es un algoritmo devorador (una vez que ha elegido a un nodo, no lo vuelve a usar)
 
Ejemplo





Ejercicio Dijkstra


Ejercicio



Conclusiones

- Dijkstra tiene una complejidad cuadrática O(n^2)
 
Algoritmo de Floyd-Warshall



Inicialización de la Matriz A

El algoritmo

- k es el pivote
 - i es el origen
 - j es el destino
 
Ejemplo 1 de Floyd-Warshall






- Lo marcado en rojo es el nodo intermedio a usar (Ej: Ir desde V1 hasta V6 usando como nodo intermedio V3)
 
Ejercicio 2


- Para hallar el camino inverso:
 

- Para ir del nodo V1 al nodo V5, usa el nodo intermedio V4
 - Para ir del nodo V1 al nodo V4, hay camino directo (-)
 - Para ir del nodo V4 al nodo V5, usa el nodo intermedio V3
 - Para ir del nodo V3 al nodo V5, hay camino directo (-)
 
Algoritmo para almacenar la secuencia de nodos que forman todos los caminos de coste mínimo
- Se puede usar para hallar el camino inverso
 

private void printPath(int i, int j)
	{
	int k = P[i][j];
	if (k>0) {
		printPath (i, k);
		System.out.print ('-' + k);
		printPath (k, j);
	}  
}
 
System.out.print (departure);
printPath (departure, arrival);
System.out.println ('-' + arrival);Floyd para rutas especiales

- Este ejercicio cayó en un examen
 
for (int k=0; k<size; k++)
	if (k in L){
		for (int i=0; i<size; i++){
			for (int j=0; j<size; j++){
				if (A[i][k] + A[k][j] < A[i][j])
				{
					A[i][j] = A[i][k] + A[k][j];
					P[i][j] := k;
				}
			}
		}
	}
}Centro de un grafo dirigido y excentricidad

Ejercicio: Obtener el centro de un grafo

Ejercicio: Hacer Floyd

FALTA
Recorrido en profundidad de un grafo (DFPrint)

boolean [] visited = new boolean [size];
 
public void resetVisited ()
{
	for (int i=0; i<size; i++)
		setVisited(i, false);
}
Ejercicio DFPrint








- Recorrido en profundidad V1: Saca por pantalla V1 V2 V4 V3
 - Recorrido en profundidad V2: Saca por pantalla V2 V4 V3
 - Recorrido en profundidad V3: Saca por pantalla V2 V4
 - Recorrido en profundidad V2: Saca por pantalla V4 V3 V2
 
Búsqueda primero en profundidad
- Detiene el recorrido una vez se cumple una condición
 

Más conceptos básicos
- Nodo fuertemente conexo y grafo fuertemente conexo
 



- árbol abarcador
 

- árbol libre abarcador de coste mínimo
 

Algoritmo de Prim

- Sólo se aplica a grafos no dirigidos
 - Busca cómo conectar todos los nodos del grafo con el árbol mínimo abarcador con el menor coste
 
Inicialización

Ejercicio 1



- Usaremos el criterio de la última arista escogida (porque tienen el mismo coste)
 



Ejercicio 2


Conclusiones
- Prim tiene complejidad O(n^3)
 

Optimización


