Documentos de Académico
Documentos de Profesional
Documentos de Cultura
ALGORITMOS DE
TEORIA DE GRAFOS
1 de 11
GRAFOS
Sea V el conjunto de vértices y A el conjunto de aristas de un grafo. En nuestro caso no vamos a
permitir más que una arista por cada par de vértices, de esta forma AVxV. A estará formado por pares
(v1, v2) donde v1 y v2 son vértices de V. Si el orden de los pares importa, el grafo es dirigido, si el orden no
importa, el grafo es no dirigido.
Cada arista puede tener asociado un coste, en este caso se dice que el grafo es valorado. En el grafo
podemos tener vértices y costes de distinto tipo, por tanto vamos a tener dos parámetros.
GRAFOS
Parametros
Tipos
vertices: conjunto finito
valores:
operaciones
:valores
_+_:valores valores valores
Ecuaciones
x
+x=
Operaciones
gVacio:grafo
ponarista: grafo vertice vertice valores grafo
quitaarista: grafo vertice vertice grafo
coste_arista: grafo vertice vertice valores
hay_arista: grafo vertice vertice booleano
sucesores: grafo vertice CONJUNTO(vértices)
predecesores: grafo vertice CONJUNTO(vértices)
ecuaciones
u1=u2 v1=v2 ponarista(ponarista(g, u1, v1, c1), u2, v2, c2) = ponarista(g, u2, v2, c2)
u1u2 v1v2
ponarista(ponarista(g, u1, v1, c1), u2, v2, c2) = ponarista(ponarista(g, u2, v2, c2), u1, v1, c1)
quitaarista(gvacio, u, v) = gvacio
u1=u2 v1=v2 quitaarista(ponarista(g, u1, v1, c1), u2, v2) = quitaarista(g, u2, v2)
u1u2 v1v2
quitaarista(ponarista(g, u1, v1, c1), u2, v2) = ponarista(quitaarista(g, u2, v2), u1, v1, c1)
coste_arista(gvacio, u, v) =
u1=u2 v1=v2 coste_arista(ponarista(g, u1, v1, c1), u2, v2) = c1
u1u2 v1v2 coste_arista(ponarista(g, u1, v1, c1), u2, v2) = coste_arista(g, u2, v2)
hayarista(g, u, v) = ¬(coste_arista(g, u, v) =
sucesores(gvacio, u) = cjto_vacio
u1=u2 sucesores(ponarista(g, u1, v1, c1), u2) = añadir(v1, sucesores(g, u2))
u1u2 sucesores(ponarista(g, u1, v1, c1), u2) = sucesores(g, u2)
predecesores(gvacio, v) = cjto_vacio
v1=v2 predecesores(ponarista(g, u1, v1, c1), v2) = añadir(u1, predecesores(g, v2))
v1v2 predecesores(ponarista(g, u1, v1, c1), v2) = predecesores(g, v2)
Implementación.
En la práctica la especificación anterior sirve de poco para la impleentación. Tenemos dos maneras de
implementar las aristas de un grafo. Si consideramos que los nodos de un grafo se puede representar por
un conjunto de la forma V={1, 2, 3,..., n}, las aristas las podemos representar de los modos siguiente:
a) Matriz de costes, o matriz de adyacencia.
Se utiliza una matriz M de n filas y n columnas donde cada entrada M(i, j) es el coste de la
arista que parte de i hasta j. Si no hay arista entre i y j entonces M(i, j) = .
De esta forma, el acceso a cada arista tiene coste constante, (1).
Para buscar los vértices adyacentes, tanto sucesores o predecesores, consiste en recorrer una
fila o una columna, por tanto tendrá coste lineal, (n).
Si el grafo es denso, habrá muchas aristas, entonces la matriz anterior tendrá pocos infinitos y
es una buena representación.
Algoritmos de Teoria de grafos M. C. Justino Ramiréz Ortegón
b) Lista de adyacencia.
Utilizamos un vector g[1..n] en los que cada g(i) es una lista de los sucesores de i.
Para buscar los sucesores de i tan solo hay que recorrer la lista g(i), coste lineal, (n).
Para buscar una arista también basta con recorrer la lista, coste lineal, (n).
Tenemos que calcular el camino con coste mínimo para ir de i a j. Si la solución es entonces no hay
camino posible entre i y j.
Vamos a denotar como R(i, j) = {i, x1, x2, ..., xh, j} a un camino que nos lleva de i a j.
Si el camino mínimo que lleva de i a j pasa por k y es R(i, j) = R(i, k) ++ R(k, j) entonces los caminos
R(i, k) y R(k, j), que llevan de i a k y de k a j también son mínimos. De no ser así sería porque hay dos
caminos R’(i, k) y R’(k, j) que nos lleva de i a k y de k a j cuyo coste es menor que los originales.
Entonces el coste de R(i, j) es mayor que la suma de ambos costes y por tanto mayor, R(i, j) no podría ser
el camino de coste mínimo.
Además es fácil comprobar que el camino mínimo no puede contener ciclos, de ser así los podríamos
eliminar obteniendo un camino con coste menor.
Consideramos Ck(i, j) coste del camino mínimo que puede pasar por los nodos 1, 2,..., k. La solución
del problema será C(i, j) = Cn(i, j).
Si el camino mínimo Ck(i, j) no pasa por k Ck(i, j) = Ck-1(i, j).
Si el camino mínimo Ck(i, j) pasa por k Ck(i, j) = Ck-1(i, k) + Ck-1(k, j).
Por tanto, para el coste del camino mínimo que lleva de i a j que puede pasar por los nodos 1, 2, ..., k
es: Ck(i, j) = min{ Ck-1(i, j), Ck-1(i, k) + Ck-1(k, j)}.
El caso básico es no pasar por ningún nodo, por tanto C0(i, j) debe ser el coste de la arista entre i y j o
si no existe esta arista, en cualquier caso C0(i, j) = G(i, j).
Observemos que el coste en espacio para almacenar esta información es (n3), se trata de información
que depende de 3 elementos, el vértice origen, el destino y el vértice más alto por el que se puede pasar.
Sin embargo este coste se puede optimizar teniendo en cuenta como se calculan las entradas de las
matrices:
C0(i, i) = G(i, i) = 0
Ck(i, i) = min{ Ck-1(i, i), Ck-1(i, k) + Ck-1(k, j)}=0
Por tanto para los elementos de la diagonal principal siempre valen 0. No cambian.
Ck(i, k) = min{ Ck-1(i, k), Ck-1(i, k) + Ck-1(k, k)} = min{ Ck-1(i, k), Ck-1(i, k)} = Ck-1(i, k)
Este tipo de elementos tampoco cambian.
Ck(k, j) = min{ Ck-1(k, j), Ck-1(k, k) + Ck-1(k, j)} = min{ Ck-1(k, j), Ck-1(k, j)} = Ck-1(k, j)
Este tipo de elementos tampoco cambian.
El resto de elementos es
Ck(i, j) = min{ Ck-1(i, j), Ck-1(i, k) + Ck-1(k, j)}.
Ck(r, s) = min{ Ck-1(r, s), Ck-1(r, k) + Ck-1(k, s)}.
Lo que vamos a hacer es tener una sola matriz C y sobre ella ir calculando sucesivamente las Ck,
vamos a comprobar que se obtiene el resultado requerido, la operación que se realiza es la siguiente:
C=G
Para k = 1 hasta n hacer
Para i=1 hasta n hacer
Para j=1 hasta n hacer
3 de 11
C(i, j) = min{ C (i, j), C(i, k) + C(k, j)};
Fpara;
Fpara;
fpara
Falta comprobar que si antes de la ejecución del bucle para i se verifica que la asignación C = C k-1, tras
la ejecución del bucle se verifica que C = Ck.
Para i = j
C(i, i) = min{C (i, i), C(i, i) + C(i, i)} = C(i, i), No varía
Efectivamente Ck(i, i) = 0 y se mantiene constante para todo k.
Para j = k; ik
Ck(i, k) = min{Ck-1(i, k), Ck-1(i, k) + C k-1(k, k)} = min{ Ck-1(i, k), Ck-1(i, k)}= Ck-1(i, k) = C(i, k)
Para i = k; jk
Ck(k, j) = min{Ck-1(k, j), Ck-1(k, k) + C k-1(k, j)} = min{Ck-1(k, j), C k-1(k, j)}= C k-1(k, j) = C(k, j)
Para i, j, k distintos
Ck(i, j) = min{Ck-1(i, j), Ck-1(i, k) + C k-1(k, j)} = min{Ck-1(i, j), C(i, k) + C(k, j)}
El problema se presenta aquí, puede ocurrir que al cambiar el valor de C(i, j) para actualizarlo a
Ck(i, j) si necesitamos posteriormente Ck-1(i, j) no disponemos de él. Pero esto no ocurre nunca,
porque para calcular Ck(i, j) necesitamos los valores de Ck-1(i, j), Ck-1(i, k), Ck-1(k, j). Los dos
últimos no varían y el primero es el que vamos a sustituir, pero solo lo utilizamos ahora y al
calcular el resto de las entradas no lo necesitamos.
Por tanto podemos sustituir todos los elementos de la matriz conservando toda la información que
necesitamos.
Algoritmo de Floyd.
Proc floyd(G[1..n, 1..n], C[1..n, 1..n] , P[1..n, 1..n]);
C:=G; P:=[0];
Para k=1 hasta n hacer
Para i=1 hasta n hacer
Para j=1 hasta n hacer
Si C(i, k)+C(k, j) < C(i, j) entonces
C(i, j) = C(i, k) + C(k, j)
P(i, j) = k
Fsi;
Fpara;
Fpara;
Fpara;
Fproc;
Lo único que puede quedar un poco oscuro es el objetivo de la matriz P. Esta matriz es la que se
utiliza para conocer el camino concreto. Para hacerlo se actúa así, si queremos conocer el camino mínimo
entre i y j, calculamos las matrices C y P por el algoritmo anterior, a continuación tomamos P(i, j) = k.
Esto significa que el camino mínimo que lleva de i a j pasa por k. Necesitamos conocer el camino mínimo
de i a k y de k a j, pero como ya hemos calculado las matrices C y P, consultamos P(i, k) = r y P(k, j) =s.
A continuación consultamos P(i, r), P(r, k), P(k, s) y P(s, j) ... etc.
Ojo, si C(i, j) = , no hay camino de i a j.
Algoritmo de Dijkstra.
Este algoritmo se utiliza para calcular el camino mínimo con origen fijo. Como antes, consideramos
que el conjunto de vértices es V={1, 2,..., n} y por comodidad consideramos que el origen del camino es
1.
El algoritmo de Dijkstra sigue la estrategia devoradora.
Consideramos el conjunto de candidatos que son los vértices a los que deseamos llegar, inicialmente
este conjunto es {2,..., n}.
La estrategia que vamos a seguir es elegir el nodo más cercano a los elegidos. Vamos a tener un vector
D[1..n] donde D[1]=0 y D[i] = “distancia a los elegidos” para i=2,3, ..., n.
Algoritmos de Teoria de grafos M. C. Justino Ramiréz Ortegón
El candidato elegido es aquel v para el cual D[v] es mínimo cuando v no está elegido. A continuación
se deben actualizar las distancias de los vértices no elegidos.
Proc dijkstra(G[1..n, 1..n], D[1..n], P[1..n]);
D[1]:=0; P:=[1];
C:={2, 3, ..., n}; Esta es la inicialización
Para i:=2 hasta n
D[i]:=G[1, i];
Fpara;
Repetir n-2 veces
V:= vértice en C con D[v] mínimo;
C:=C – {v};
Para cada wC hacer Cada vez añadimos un vértice a 1
los elegidos. 10 50
Si D[v] + G[v, w] < D[w] entonces
D[w] := D[v] + G[v, w];
P[w] := v 5 100 30 2
Fsi;
Fpara; 20
5
Frepetir;
Fproc; 4 3
50
Supongamos por hipótesis de inducción que son ciertos 1) y 2). Vamos a ver que pasa al ejecutar otra
vez el bucle.
Tomamos vC tal que D[v]= min {D[w] con vC}
C = C –{v}
S = S {v}
Para cada w
1) Sea iS.
Si iv entonces por H.I. D[i] es el coste mínimo de ir desde 1 a i.
Si i=v entonces por H.I. D[v] es el coste mínimo de ir desde 1 a i por nodos de S.
Tenemos que ver que al añadir v a S y quitarlo de C, D[v] es el camino mínimo. Supongamos
que no fuese así, esto sería por que hay un camino mínimo que no es especial y que lleva de 1 a
i. Como el camino no es especial, hay un nodo x por el que pasa el camino y que no es de S y
todos los anteriores nodos por los que se pasa son de S.
El coste de este camino se puede calcular así d(1, v) = d(1, x) + d(x, v), donde el camino de 1 a x
si es especial. Se tiene que D[v]>d(1, v) = d(1, x) + d(x, v) d(1,x) = D[x] por H.I., pero xS
xC D[v]>D[x]D[v] porque en S se van introduciendo los elementos con D[v] mínimo.
Absurdo.
1
Un camino ESPECIAL de i a j con iS y jS es un camino que lleva de i a j pero pasando solo por nodos
de S
5 de 11
2) Sea iC.
Al añadir un nuevo elemento a S pueden ocurrir dos cosas, que el camino especial para i siga
siendo el mismo, porque al añadir v a S solo aparezcan caminos especiales para i con coste
mayor, o que el camino especial cambie, en cuyo caso este nuevo camino especial debe pasar por
v, de no ser así, ese camino especial ya habría sido elegido en algún paso anterior del bucle.
a) si el camino especial mínimo no cambia, se da que D[i] = min{D[i], D[v]+G[v, i]} = D[i] y
por hipótesis de inducción este el coste del camino especial mínimo que solo pasa por nodos
de S.
b) si el camino especial mínimo cambia, tenemos dos posibilidades, que v sea el último nodo
justo antes de i o que no lo sea
i) si v es el último nodo antes de i entonces D[i] = min{D[i], D[v]+G[v, i]} = D[v]+G[v, i]
ii) si v no es el último nodo antes de i entonces sea x el último nodo antes de i, se tiene que
d(1, i) = d(1, v)+d(v, x)+G[x, i], pero entonces
Si x es el último nodo antes de i que está en S entonces D[x] es el camino mínimo que
lleva de 1 a x, y x ya estaba en S antes de v D[i] ya es el coste de un camino especial
mínimo.
Eficiencia.
Implementación del conjunto de candidatos.
Dado que se manejan mínimos y se toman ordenadamente, podemos distinguir 2 posibilidades:
- Implementarlo mediante una matiz de adyacencia y un vector de booleanos, C[1..n] donde
C[i]= True significa que i es uno de los vértices escogidos.
El coste de inicialización es (n).
El cálculo del mínimo tiene coste O(n) y la actualización de los D[i], O(n). Como esta tarea
hay que repetirla n-2 veces el coste del bucle es (n2).
El coste total del algoritmo es (n2).
- Implementarlo con un montículo de mínimos según D, con una lista de adyacencia.
Coste de la inicialización, con un montículo (n).
Elección del mínimo y eliminación O(log n), repetido n-2 veces (n log n)
Actualización de D(i), mediante flotar, O(log n), repetir por cada arista a (a log n)
El coste total del bucle será ((n+a) log n)
Suponemos que na, cada vértice tiene al menos una arista, si el grafo es denso, a se aproxima
a n2 y el coste es aproximadamente (n2 log n), pero si el grafo es disperso, a se aproxima a n
y el coste será (n log n).
Vamos a ver una comparativa entre los algoritmos de Dijkstra y Floyd. Ojo, Dijkstra solo calcual la
distancia desde un vértice al resto, mientras que Floyd calcula todas las distancias entre todos los vértices.
Para poderlos comparar debemos calcular todas las distancias mediante Dijkstra mediante llamadas
sucesivas al algorítmo
Dijkstra Floyd
Denso (n ) n llamadas (n )
2 3
(n3)
Disperso (n log n) n llamadas (n2 log n)
Proposición.
Un grafo conexo y sin ciclos, un árbol, con n vértices tiene n-1 aristas.
Demostración.
Por inducción en el número de vértices.
|V|=1. Como en V solo hay un vértice no podemos añadir ninguna arista, porque en caso de añadirlo
siempre se formarán ciclos. El número de aristas es 0 = 1-1
Algoritmos de Teoria de grafos M. C. Justino Ramiréz Ortegón
Supongamos que si |V| = n-1 y G =(V, A) es un grafo conexo sin ciclos |A| = (n-1)-1, para n1
Sea V un conjunto de vértices tal que |V| = n>1 y G = (V, A) es un grafo conexo sin ciclos, sea xV,
tal que x tiene una sola arista e en A. Construimos V’ = V – {x}, A’ = A – {e}. Tenemos que G’=(V’, A’)
es un grafo conexo, sin ciclos y |V’| = n-1
Sean v, w dos vértices de G’. Entonces v y w son dos vértices de G, como G es conexo existe un
camino en G que lleva de v a w. Tenemos que ver que este camino no utiliza la arista a.
Efectivamente, porque la arista e tiene como un de sus extremos x y de x no sale ninguna otra
arista, esto significa que cualquier camino que utilice la arista e o bien parte de x o bien llega a x.
Como ni v ni w pueden ser el vértice x, porque xG’, el camino que lleva de v a w no puede
utilizar la arista e, entonces todas las aristas que utiliza este camino están en G’ = G-{e} existe
un camino que lleva de v a w en G’.
En G’ no puede haber ciclos porque no los había en G.
Por hipótesis de inducción, como |V’| = n-1 y es grafo conexo y sin ciclos |A’| = n-2 |A| = n-1.
El único detalle es demostrar que en G siempre existe un vértice del que parte una única arista, si |V|
>1 y G es grafo conexo y sin ciclos. Supongamos que todos los vértices de V tienen al menos dos aristas
distintas. Como el grafo es conexo, podemos construir un camino que pasa por todos los vértices de G,
por la definición de grafo conexo. Sea el camino {x1, x2, ..., xn-1, xn} xiV todos distintos, como en xn hay
al menos dos aristas distintas debe haber otra arista (xn, y) con yxn-1. Podemos construir el camino {x1,
x2, ..., xn-1, xn, y}, pero yV existe un k{1,..., n-2} tal que y = xk. Entonces tenemos el camino {x1, x2,
..., xk-1, xk, xk+1,...,xn-1, xn, xk} existe un ciclo {xk, xk+1,...,xn-1, xn, xk}. Absurdo.
Cada grafo se divide en todas sus componentes conexas. Dado G = (V, A), (V’, T’) es una componente
conexa de G si se verifican tres cosas:
1) V’V
2) T’A
3) (V’, A’) es arbol conexo.
DEFINICIÓN.
Un BOSQUE DE RECUBRIMIENTO B de G = (V, T) es un conjunto B = {(V1, T1), (V2, T2), ..., (Vk, Tk)}
k k
tal que cada (Vi, Ti) es una componente conexa de G , V Vi y T Ti .
i 1 i 1
Dado un bosque de recubrimiento, se dice que es PROMETEDOR si se puede completar de forma que
lleve a un árbol de recubrimiento de coste mínimo.
Teorema.
Sea (V, B) un bosque de recubrimiento, (V, B) = (V1, T1)(V2, T2) ... (Vk, Tk), donde (Vi, Ti) son
árboles de recubrimiento para i=1,2,...,k. Sea i0{1, 2, ..., k}. Sea eB, e = (u,v) que sale de Vi 0
, Ti 0 ,
u Vi 0 y v Vi 0
por tanto o .
u V y v V
i0 i0
Si e tiene coste mínimo (V, B{e}) es prometedor.
Demostración.
e no crea ciclos en (V, B{e}).
u Vi 0 y v Vj0
Como e sale de Vi 0
, Ti 0 , entonces existe j0{1, 2, ..., k} tal que i0j0 y
o
u V y v V
j0 i0
Supongamos que e crea un ciclo en (V, B{e}) entonces en este ciclo interviene e, que parte cuyos
extremos están en componentes conexas distintas, entonces tiene que haber algún camino que una las
dos componentes conexas y que no utiliza la arista e. Entonces las dos componentes conexas forman
una sola.
Vamos a ver que (V, B) está contenido en (V, T) un ARCM.
Si eT entonces B{e}T (V, B{e}) es prometedor
7 de 11
Si eT entonces como T tiene n-1 aristas T{e} tiene n aristas no es un árbol hay un ciclo
que incluye a esa arista tiene que existir otra arista d que salga de Vi 0 (o Vj0 ) que forma parte
del mismo ciclo. Además coste(e)coste(d), porque es la arista de coste mínimo que sale de
Vi 0 (o Vj0 ) . Construimos T’ = (T{e}) – {d}. En (V, T’) ya no hay ciclos, es un árbol además
tenemos que el coste de T’ es c(T’) = c(T{e}) – {d}) = c(T) + c(e) – c(d) c(T), pero T es ARCM
c(T)c(T’) c(T) = c(T’) T’ = (T{e}) – {d} es ARCM.
Falta comprobar que B{e} T’ = (T{e}) – {d}
- e(T{e}) – {d}
- e’B e’ d porque d sale de Vi 0 (o Vj0 ) no puede estar en B porque todas las
aristas de B quedan dentro de la misma componente conexa. Por tanto e’T–
{d}(T{e}) – {d}
Algoritmo general.
El esquema básico que siguen los algoritmos para el cálculo de ARCM, es el siguiente:
(V, B) = (V, )
mientras ncc(B)>1 {ncc(B) es el número de componentes conexas de B}
(V, B) = (V1, T1) (V2, T2) ... (Vk, Tk)
escoger i: 1ik
e = arista de coste mínimo que sale de (Vi, Ti)
Unir Vi y Vj con e
fmientras
A partir de aquí los algoritmos concretos introducen detalles sobre como hacer las tareas que aquí
quedan de un modo abstracto.
Vamos a comprobar que el caso base es prometedor. Como el grafo es conexo, el (V, T) ARCM existe.
T (V, )(V, T) (V, ) es prometedor.
Habiendo comprobado el caso base y utilizando el teorema anterior tenemos demostrada la corrección
del algoritmo anterior y con ello la de todos los algoritmos derivados de él, en particular los dos que
vamos a estudiar el de Prim y el de Kruskal.
Algoritmo de Prim.
Transformamos el “escoger i: 1ik” en tomar i=1. De esta forma tenemos un árbol que va a ir
creciendo, al que le vamos a ir añadiendo nodos. El resto de las componentes conexas son vértices
aislados.
Se este modo podemos dividir los nodos en alegidos y sueltos. La elección de la arista es equivalente
ahora a la elección del nodo suelto más cercano a los elegidos. Como vemos este algoritmo es muy
similar al de Dijkstra.
Func prim_abstracto(G = (V, A)) dev T:cjto de aristas;
T := ; Observese que cada vuelta del bucle añade uno y solo uno de
B := {1}; los vértices no elegidos, como queremos que al final haya n
Mientras |B| n hacer nodos elegidos debemos ejecutar el bucle n-1 veces. Se
e = (u, v) tal que uB, vB y coste(e) sea mínimo puede sustituir el bucle mientras por un repetir n-1 veces ...
T = T {e}
B = B {v}
fmientras
ffunc
Si utilizamos montículos, de aristas, produce un coste (a log n) donde a es el número de aristas.
Algoritmo de Kruskal.
El algoritmo de Kruskal parte también del algoritmo general, pero a diferencia del de Prim que hace
crecer un árbol y las demás componentes conexas son unitarias, en Kruskal escogemos la arista de coste
mínimo de todas las existentes, no solo las que salen de una componente concreta.
Examinemos como trabaja el algoritmo de Kruskal. Se escoge una arista e=(u, v) con coste mínimo.
Deben de saber a que componente conexa pertenece u y a que componente pertenece v, comp(u) y
comp(v) respectivamente.
Si comp(u)comp(v) podemos unir ambas componentes en una sola utilizando la arista e.
Si comp(u)=comp(v) no debemos añadir ninguna arista porque como u y v están en la misma
componente conexa, al añadir una nueva arista se formaría un ciclo.
Dado que se debe escoger la arista de coste mínimo, conviene ordenar las aristas según su coste.
Vamos a ver el algorítmo de Kruskal:
Func kruskal(({1,2,...,n}, A)) dev T:cjto de aristas
Ordenar(A);
T:=
Inicializar ECC con n conjuntos unitarios; {ECC es la estructura de componentes conexas}
Mientras |T|<n-1 hacer
(u, v) := arista de coste mínimo;
A := A – {(u, v)};
Si buscar(u)buscar(v) entonces
T:=T{(u, v)}
unir(u, v);
Fsi;
Fmientras;
Ffunc;
Podemos ver que la estructura de datos que vamos a manejar para almacenar las componentes conexas
debe tener al menos estás dos operaciones:
- buscar(u) que devuelve la componente conexa a la que pertenece un vértice
- unir(u, v) que debe unir las componentes conexas a las que pertenecen u y v.
Si definimos la relación R tal que uRv u y v están en la misma componente conexa, tenemos que la
relación R es de equivalencia. Así tenemos que las dos operaciones anteriores equivalen a calcular la clase
a la que pertenece un vértice y a la unión de clases respectivamente. Por esta razón vamos a utilizar una
estructura que, según el autor, se denomina Partición, o conjuntos disjuntos o relación de equivalencia.
Quedan bastantes detalles a decidir para la implementación, como por ejemplo cual va a ser la
representación de cada clase. Dado que cada elemento está en una y solo en una de las clases, podemos
tomar como representante cualquiera de los elementos de la clase. Para unificar criterios vamos a utilizar
como representante de la clase al menos elemento de la clase.
Hemos de tener en cuenta a la hora de establecer la implementación que la utilización de esa
estructura optimice el algoritmo con el que lo vamos a utilizar.
Una primera aproximación a la implementación sería representar a las clases de equivalencia como un
vector Cjto[1..n], de forma que Cjto[i] = j significa que el elemento i pertenece a la clase de equivalencia
representada por j. Con esta implementación, las operaciones requeridas quedan del modo siguiente:
Fun buscar(u) dev v v:=cjto(u)
9 de 11
Ffunc; Proc unir(u, v);
Complejidad (1) i:=min(u,v);
j:=max(u,v);
para k=1 hasta n hacer
si cjto(k)=j entonces
cjto(k):=i;
fpara;
Fproc;
Complejidad (n)
Si se realiza n secuencias de operaciones buscar y unir, tal y como ocurre en el algoritmo de Kruskall,
el coste será (n2).
11 de 11