Vous êtes sur la page 1sur 10

Grafo (estructura de datos)

Saltar a: navegación, búsqueda

Un grafo de 6 vértices y 7 aristas.

Un grafo en el ámbito de las ciencias de la computación es una estructura de datos, en


concreto un tipo abstracto de datos (TAD), que consiste en un conjunto de nodos (también
llamados vértices) y un conjunto de arcos (aristas) que establecen relaciones entre los
nodos. El concepto de grafo TAD desciende directamente del concepto matemático de
grafo.

Informalmente se define como G = (V, E), siendo los elementos de V los vértices, y los
elementos de E, las aristas (edges en inglés). Formalmente, un grafo, G, se define como un
par ordenado, G = (V, E), donde V es un conjunto finito y E es un conjunto que consta de
dos elementos de V.

Índice
 1 Formas de representación
 2 Especificación de los tipos abstractos de datos de un grafo no dirigido
o 2.1 Generadores
o 2.2 Constructores
o 2.3 Selectores
 3 Enlaces externos

Formas de representación
Existen diferentes implementaciones del tipo grafo: con una matriz de adyacencias (forma
acotada) y con listas y multilistas de adyacencia (no acotadas).

 Matriz de adyacencias: se asocia cada fila y cada columna a cada nodo del grafo,
siendo los elementos de la matriz la relación entre los mismos, tomando los valores
de 1 si existe la arista y 0 en caso contrario.
Joseph Kruskal
Saltar a: navegación, búsqueda

Joseph B. Kruskal (29 de enero de 1928 – Maplewood, Nueva Jersey, 19 de septiembre de


2010)1 fue un matemático y estadístico estadounidense.

Índice
 1 Biografía
 2 Familia
 3 Referencias
 4 Enlaces externos

Biografía
Investigador del Math Center (Bell-Labs), en 1956 descubrió un algoritmo para la
resolución del problema del árbol recubridor mínimo, el cual es un problema típico de
optimización combinatoria, que fue considerado originalmente por Otakar Boruvka (1926)
mientras estudiaba la necesidad de electrificación rural en el sur de Moravia en
Checoslovaquia.

El objetivo del algoritmo de Kruskal es construir un árbol (subgrafo sin ciclos) formado por
arcos sucesivamente seleccionados de mínimo peso a partir de un grafo con pesos en los
arcos.

Un árbol (spanning tree) de un grafo es un subgrafo que contiene todos sus vértices o
nodos. Un grafo puede tener múltiples árboles. Por ejemplo, un grafo completo de cuatro
nodos (todos relacionados con todos) tendría 16 árboles.

La aplicación típica de este problema es el diseño de redes telefónicas. Una empresa con
diferentes oficinas, trata de trazar líneas de teléfono para conectarlas unas con otras. La
compañía telefónica le ofrece esta interconexión, pero ofrece tarifas diferentes o costes por
conectar cada par de oficinas. Cómo conectar entonces las oficinas al mínimo coste total.

La formulación del MST también ha sido aplicada para hallar soluciones en diversas áreas
(diseño de redes de transporte, diseño de redes de telecomunicaciones - TV por cable,
sistemas distribuidos, interpretación de datos climatológicos, visión artificial - análisis de
imágenes - extracción de rasgos de parentesco, análisis de clusters y búsqueda de
superestructuras de quasar, plegamiento de proteínas, reconocimiento de células
cancerosas, y otros).

Otra aplicación menos obvia es que el árbol de coste total mínimo puede ser usado como
solución aproximada al problema del viajante de comercio, el cual es NP-completo. La
manera formal de definir este problema es encontrar la trayectoria más corta para visitar
cada punto al menos una vez. Nótese que si se visitan todos los puntos exactamente una
vez, lo que se tiene es un tipo especial de árbol. En el ejemplo anterior, 12 de los 16 árboles
son trayectorias de este tipo. Si se tiene una trayectoria que visita algunos vértices más de
una vez, siempre se puede soltar algunos nodos del árbol. En general el peso del árbol total
mínimo es menor que el del viajante de comercio, debido a que su minimización se realiza
sobre un conjunto estrictamente mayor. Existen diferentes algoritmos y maneras de usar el
árbol de coste total mínimo para encontrar la solución al problema del viajante de comercio
(con resultados cercanos al óptimo).

 Lista de adyacencias: se asocia a cada nodo del grafo una lista que contenga todos
aquellos nodos que sean adyacentes a él.

Especificación de los tipos abstractos de datos de un


grafo no dirigido
Generadores
Crear un grafo vacío: Devuelve un grafo vacío.

 op crearGrafo : -> Grafo [ctor] .

Añadir una arista: Dado un grafo, añade una relación entre dos nodos de dicho grafo.

 op añadirArista : Grafo Nodo Nodo -> [Grafo] [ctor] .

Añadir un nodo: Dado un grafo, incluye un nodo en él, en caso en el que no exista
previamente.

 op añadirNodo : Grafo Nodo -> Grafo [ctor] .

Constructores

Borrar nodo: Devuelve un grafo sin un nodo y las aristas relacionadas con él. Si dicho nodo
no existe se devuelve el grafo inicial.

 op borrarNodo : Grafo Nodo -> Grafo .

Borrar arista: Devuelve un grafo sin la arista indicada. En caso de que la arista no exista
devuelve el grafo inicial.

 op borrarArista : Grafo Nodo Nodo -> Grafo .

Selectores

Grafo Vacio: Comprueba si un grafo no tiene ningún nodo.

 op esVacio : Grafo -> Bool .

Contener Nodo: Comprueba si un nodo pertenece a un grafo.

 op contiene : Grafo Nodo -> Bool .

Adyacentes: Comprueba si dos nodos tienen una arista que los relacione.

 op adyacentes : Grafo Nodo Nodo -> Bool .

Para la especificación de un grafo dirigido tenemos que modificar algunas de las


ecuaciones de las operaciones borrarArista y añadirArista para que no se considere el caso
de aristas bidireccionales.

Y sustituir la operación adyacentes por:

 op predecesor : Grafo Nodo Nodo -> Bool .


 op sucesor : Grafo Nodo Nodo -> Bool .

Algoritmo de Kruskal
Saltar a: navegación, búsqueda

El algoritmo de Kruskal es un algoritmo de la teoría de grafos para encontrar un árbol


recubridor mínimo en un grafo conexo y ponderado. Es decir, busca un subconjunto de
aristas que, formando un árbol, incluyen todos los vértices y donde el valor total de todas
las aristas del árbol es el mínimo. Si el grafo no es conexo, entonces busca un bosque
expandido mínimo (un árbol expandido mínimo para cada componente conexa). El
algoritmo de Kruskal es un ejemplo de algoritmo voraz.

Un ejemplo de árbol expandido mínimo. Cada punto representa un vértice, el cual puede ser
un árbol por sí mismo. Se usa el Algoritmo para buscar las distancias más cortas (árbol
expandido) que conectan todos los puntos o vértices.

Funciona de la siguiente manera:

 se crea un bosque B (un conjunto de árboles), donde cada vértice del grafo es un
árbol separado
 se crea un conjunto C que contenga a todas las aristas del grafo
 mientras C es no vacío
o eliminar una arista de peso mínimo de C
o si esa arista conecta dos árboles diferentes se añade al bosque, combinando
los dos árboles en un solo árbol
o en caso contrario, se desecha la arista

Al acabar el algoritmo, el bosque tiene un solo componente, el cual forma un árbol de


expansión mínimo del grafo.

Este algoritmo fue publicado por primera vez en Proceedings of the American
Mathematical Society, pp. 48–50 en 1956, y fue escrito por Joseph Kruskal.
Índice
 1 Pseudocódigo
 2 Código en C++
 3 Complejidad del algoritmo
 4 Demostración de la correctitud
 5 Ejemplo
 6 Referencias
 7 Enlaces externos

Pseudocódigo
function Kruskal(G)
Para cada v en V[G] hacer
Nuevo conjunto C(v) ← {v}.
Nuevo heap Q que contiene todas las aristas de G, ordenando por su
peso.
Defino un arbol T ← Ø
// n es el número total de vértices
Mientras T tenga menos de n-1 vertices hacer
(u,v) ← Q.sacarMin()
// previene ciclos en T. agrega (u,v) si u y v están diferentes
componentes en el conjunto.
// Nótese que C(u) devuelve la componente a la que pertenece u.
if C(v) ≠ C(u) then
Agregar arista (v,u) to T.
Merge C(v) y C(u) en el conjunto
Return arbol T

Código en C++
// Declaraciones en el archivo .h
int cn; //cantidad de nodos
vector< vector<int> > ady; //matriz de adyacencia

// Devuelve la matriz de adyacencia del árbol mínimo.


vector< vector<int> > Grafo :: kruskal(){
vector< vector<int> > adyacencia = this->ady;
vector< vector<int> > arbol(cn);
vector<int> pertenece(cn); // indica a que árbol pertenece el nodo

for(int i = 0; i < cn; i++){


arbol[i] = vector<int> (cn, INF);
pertenece[i] = i;
}

int nodoA;
int nodoB;
int arcos = 1;
while(arcos < cn){
// Encontrar el arco mínimo que no forma ciclo y guardar los
nodos y la distancia.
int min = INF;
for(int i = 0; i < cn; i++)
for(int j = 0; j < cn; j++)
if(min > adyacencia[i][j] && pertenece[i] !=
pertenece[j]){
min = adyacencia[i][j];
nodoA = i;
nodoB = j;
}

// Si los nodos no pertenecen al mismo árbol agrego el arco al


árbol mínimo.
if(pertenece[nodoA] != pertenece[nodoB]){
arbol[nodoA][nodoB] = min;
arbol[nodoB][nodoA] = min;

// Todos los nodos del árbol del nodoB ahora pertenecen al


árbol del nodoA.
int temp = pertenece[nodoB];
pertenece[nodoB] = pertenece[nodoA];
for(int k = 0; k < cn; k++)
if(pertenece[k] == temp)
pertenece[k] = pertenece[nodoA];

arcos++;
}
}
return arbol;
}

Complejidad del algoritmo


m el número de aristas del grafo y n el número de vértices, el algoritmo de Kruskal muestra
una complejidad O(m log m) o, equivalentemente, O(m log n), cuando se ejecuta sobre
estructuras de datos simples. Los tiempos de ejecución son equivalentes porque:

 m es a lo sumo n2 y log n2 = 2logn es O(log n).


 ignorando los vértices aislados, los cuales forman su propia componente del árbol
de expansión mínimo, n ≤ 2m, así que log n es O(log m).

Se puede conseguir esta complejidad de la siguiente manera: primero se ordenan las aristas
por su peso usando una ordenación por comparación (comparison sort) con una
complejidad del orden de O(m log m); esto permite que el paso "eliminar una arista de peso
mínimo de C" se ejecute en tiempo constante. Lo siguiente es usar una estructura de datos
sobre conjuntos disjuntos (disjoint-set data structure) para controlar qué vértices están en
qué componentes. Es necesario hacer orden de O(m) operaciones ya que por cada arista hay
dos operaciones de búsqueda y posiblemente una unión de conjuntos. Incluso una
estructura de datos sobre conjuntos disjuntos simple con uniones por rangos puede ejecutar
las operaciones mencionadas en O(m log n). Por tanto, la complejidad total es del orden de
O(m log m) = O(m log n).
Con la condición de que las aristas estén ordenadas o puedan ser ordenadas en un tiempo
lineal (por ejemplo, mediante el ordenamiento por cuentas o con el ordenamiento Radix), el
algoritmo puede usar estructuras de datos de conjuntos disjuntos más complejas para
ejecutarse en tiempos del orden de O(m α(n)), donde α es la inversa (tiene un crecimiento
extremadamente lento) de la función de Ackermann.

Demostración de la correctitud
Sea P un grafo conexo y valuado y sea Y el subgrafo de P producido por el algoritmo. Y no
puede tener ciclos porque cada vez que se añade una arista, ésta debe conectar vértices de
dos árboles diferentes y no vértices dentro de un subárbol. Y no puede ser disconexa ya que
la primera arista que une dos componentes de Y debería haber sido añadida por el
algoritmo. Por tanto, Y es un árbol expandido de P.

Sea Y1 el árbol expandido de peso mínimo de P, el cual tiene el mayor número de aristas en
común con Y. Si Y1=Y entonces Y es un árbol de expansión mínimo. Por otro lado, sea e la
primera arista considerada por el algoritmo que está en Y y que no está en Y1. Sean C1 y C2
las componentes de P que conecta la arista e. Ya que Y1 es un árbol, Y1+e tiene un ciclo y
existe una arista diferente f en ese ciclo que también conecta C1 y C2. Entonces Y2=Y1+e-f
es también un árbol expandido. Ya que e fue considerada por el algoritmo antes que f, el
peso de e es al menos igual que que el peso de f y ya que Y1 es un árbol expandido mínimo,
los pesos de esas dos aristas deben ser de hecho iguales. Por tanto, Y2 es un árbol expandido
mínimo con más aristas en común con Y que las que tiene Y1, contradiciendo las hipótesis
que se habían establecido antes para Y1. Esto prueba que Y debe ser un árbol expandido de
peso mínimo.

Otros algoritmos para este problema son el algoritmo de Prim y el algoritmo de Boruvka.

Ejemplo

Este es el grafo original. Los números de las aristas


indican su peso. Ninguna de las aristas está resaltada.
AD y CE son las aristas más cortas, con peso 5, y AD se
ha elegido arbitrariamente, por tanto se resalta.

Sin embargo, ahora es CE la arista más pequeña que no


forma ciclos, con peso 5, por lo que se resalta como
segunda arista.

La siguiente arista, DF con peso 6, ha sido resaltada


utilizando el mismo método.

La siguientes aristas más pequeñas son AB y BE, ambas


con peso 7. AB se elige arbitrariamente, y se resalta. La
arista BD se resalta en rojo, porque formaría un ciclo
ABD si se hubiera elegido.
El proceso continúa marcando las aristas, BE con peso 7.
Muchas otras aristas se marcan en rojo en este paso: BC
(formaría el ciclo BCE), DE (formaría el ciclo DEBA), y
FE (formaría el ciclo FEBAD).

Finalmente, el proceso termina con la arista EG de peso 9,


y se ha encontrado el árbol expandido mínimo.

Vous aimerez peut-être aussi