• No se han encontrado resultados

Cap´ıtulo 7: Algoritmos en grafos

N/A
N/A
Protected

Academic year: 2020

Share "Cap´ıtulo 7: Algoritmos en grafos"

Copied!
32
0
0

Texto completo

(1)

Cap´ıtulo 7: Algoritmos en grafos

Del libro: Dise˜

no de Algoritmos - Nivio Ziviani (UFMG)

Noviembre - 2011

Ingenier´ıa de Software

(2)

´Indice

1

TAD - Grafos

Implementaci´

on mediante matrices de adyacencia

Listas de adyacencia - usando punteros

2

Arbol de recubrimiento m´ınimo

´

Algoritmo de Prim

Algoritmo de Kruskal

(3)

TAD - Grafos

TAD de los grafos

Dado un grafo G = (V, A), donde V son los v´

ertices y A las aristas:

1

Crear un grafo vac´ıo.

2

Insertar una arista al grafo.

3

Verificar si existe una determinada arista en el grafo.

4

Obtener una lista de vertices adyacentes a un determinado vertice.

5

Eliminar una arista de un grafo.

6

Imprimir un grafo.

7

Obtener el n´

umero de v´

erctices del grafo.

8

Obtener la transpuesta de un grafo direccionado.

9

Obtener la arista de menor peso en el grafo.

(4)

TAD - Grafos Implementaci´on mediante matrices de adyacencia

Operaci´

on obtener lista de adyacentes

1

Verificar que lista de adyacentes de un v´

ertice se encuentra vacia.

Retorna true si la lista de adyacentes se encuentra vac´ıa.

2

Obtener el primer v´

ertice adyacente a un v´

ertice v, en caso exista

alguno. Retorna la direcci´

on del primer v´

ertice de la lista de

adyacentes de v.

3

Obtener el pr´

oximo v´

ertice adyacente a un v´

ertice v, en caso exista.

Retorna la pr´

oxima arista en que el v´

ertice v participa.

(5)

TAD - Grafos Implementaci´on mediante matrices de adyacencia

Operaci´

on obtener lista de adyacentes

Lectura de los adyacentes de una grafo:

if ( !grafo. listaAdjVacia (v ) ) {

Arista aux = grafo.primeroListaAdj (v) ;

while (aux != null ) {

int u = aux.vertice2( );

int peso = aux.peso ( ) ;

aux = grafo.proxAdj (v) ;

}

(6)

TAD - Grafos Implementaci´on mediante matrices de adyacencia

Matriz de adyacencia

(7)

TAD - Grafos Implementaci´on mediante matrices de adyacencia

Matriz de adyacencia - An´

alisis

Debe ser implementada para grafos densos, donde |A| es pr´

oximo |V |

2

Es muy util para algoritmos en que necesitamos saber con rapidez si

existe una arista conectada a dos vertices.

(8)

TAD - Grafos Implementaci´on mediante matrices de adyacencia

Matriz de adyacencia - Implementaci´

on

La inserci´on de un nuevo v´ertice se puede realizar en un tiempo constante.

package Cap7.matrizadj; public class Grafo {

public static class Arista { private int v1, v2, peso;

public Arista (int v1, int v2, int peso) { this.v1 = v1; this.v2 = v2; this.peso = peso; }

public int peso () { return this.peso; } public int v1 () { return this.v1; } public int v2 () { return this.v2; } }

private int mat[][]; private int numVertices; private int pos[];

public Grafo (int numVertices) {

this.mat = new int[numVertices][numVertices]; this.pos = new int[numVertices];

this.numVertices = numVertices;

for (int i = 0; i < this.numVertices; i++) {

for (int j = 0; j < this.numVertices; j++) this.mat[i][j] = 0; this.pos[i] = -1;

} }

public Grafo (int numVertices, int numAristas) { this.mat = new int[numVertices][numVertices]; this.pos = new int[numVertices];

this.numVertices = numVertices;

for (int i = 0; i < this.numVertices; i++) {

for (int j = 0; j < this.numVertices; j++) this.mat[i][j] = 0; this.pos[i] = -1;

} }

(9)

TAD - Grafos Implementaci´on mediante matrices de adyacencia

Matriz de adyacencia - Implementaci´

on

public void insertaArista (int v1, int v2, int peso) { this.mat[v1][v2] = peso;

}

public boolean existeArista (int v1, int v2) { return (this.mat[v1][v2] > 0);

}

public boolean listaAdjVacia (int v) { for (int i =0; i < this.numVertices; i++)

if (this.mat[v][i] > 0) return false; return true;

}

public Arista primeiroListaAdj (int v) { this.pos[v] = -1; return this.proxAdj (v); }

public Arista proxAdj (int v) { this.pos[v] ++;

while ((this.pos[v] < this.numVertices) &&

(this.mat[v][this.pos[v]] == 0)) this.pos[v]++; if (this.pos[v] == this.numVertices) return null;

else return new Arista (v, this.pos[v], this.mat[v][this.pos[v]]); }

public Arista eliminaArista (int v1, int v2) {

if (this.mat[v1][v2] == 0) return null; // @{\it Arista n\~ao existe}@ else {

Arista arista = new Arista (v1, v2, this.mat[v1][v2]); this.mat[v1][v2] = 0; return arista;

} }

(10)

TAD - Grafos Implementaci´on mediante matrices de adyacencia

Matriz de adyacencia - Implementaci´

on

public void imprimir () { System.out.print (" ");

for (int i = 0; i < this.numVertices; i++) System.out.print (i + " ");

System.out.println ();

for (int i = 0; i < this.numVertices; i++) { System.out.print (i + " ");

for (int j = 0; j < this.numVertices; j++) System.out.print (this.mat[i][j] + " "); System.out.println ();

} }

public int numVertices () { return this.numVertices; } public Grafo grafoTranspuesto () {

Grafo grafoT = new Grafo (this.numVertices); for (int v = 0; v < this.numVertices; v++)

if (!this.listaAdjVacia (v)) {

Arista adj = this.primeiroListaAdj (v); while (adj != null) {

grafoT.insertaArista (adj.v2 (), adj.v1 (), adj.peso ()); adj = this.proxAdj (v); } } return grafoT; } }

(11)

TAD - Grafos Implementaci´on mediante matrices de adyacencia

Matriz de adyacencia - Implementaci´

on

public void imprimir () { System.out.print (" ");

for (int i = 0; i < this.numVertices; i++) System.out.print (i + " ");

System.out.println ();

for (int i = 0; i < this.numVertices; i++) { System.out.print (i + " ");

for (int j = 0; j < this.numVertices; j++) System.out.print (this.mat[i][j] + " "); System.out.println ();

} }

public int numVertices () { return this.numVertices; } public Grafo grafoTranspuesto () {

Grafo grafoT = new Grafo (this.numVertices); for (int v = 0; v < this.numVertices; v++)

if (!this.listaAdjVacia (v)) {

Arista adj = this.primeiroListaAdj (v); while (adj != null) {

grafoT.insertaArista (adj.v2 (), adj.v1 (), adj.peso ()); adj = this.proxAdj (v); } } return grafoT; } }

(12)

TAD - Grafos Listas de adyacencia - usando punteros

Listas de adyacencia - usando punteros

(13)

TAD - Grafos Listas de adyacencia - usando punteros

Listas de adyacencia - usando punteros

Se utiliza un arreglo de |V | listas, una para cada v´

ertice de V .

Para cada u ∈ V , adj[u] contiene todos los v´

ertices adyacentes a u en

G.

Posee una complejidad de espacio de O(|V | + |A|).

Indicada para grafos dispersos, donde |A| es mucho menor que |V |

2

En el peor caso puede tener un tiempo de O(|V |), es el n´

umero de

ertices adyacentes m´

aximo.

(14)

TAD - Grafos Listas de adyacencia - usando punteros

Listas de adyacencia - usando punteros - implementacion

package Cap7.listaadj.autoreferencia; import Cap3.apuntadores.Lista; public class Grafo {

public static class Arista { private int v1, v2, peso;

public Arista (int v1, int v2, int peso) { this.v1 = v1; this.v2 = v2; this.peso = peso; }

public int peso () { return this.peso; } public int v1 () { return this.v1; } public int v2 () { return this.v2; } }

private static class Celda { int vertice, peso;

public Celda (int v, int p) {this.vertice = v; this.peso = p;} public boolean equals (Object obj) {

Celda item = (Celda) obj;

return (this.vertice == item.vertice); }

}

private Lista adj[]; private int numVertices;

(15)

TAD - Grafos Listas de adyacencia - usando punteros

Listas de adyacencia - usando punteros - implementacion

public Grafo (int numVertices) {

this.adj = new Lista[numVertices]; this.numVertices = numVertices; for (int i = 0; i < this.numVertices; i++) this.adj[i] = new Lista(); }

public Grafo (int numVertices, int numAristas) {

this.adj = new Lista[numVertices]; this.numVertices = numVertices; for (int i = 0; i < this.numVertices; i++) this.adj[i] = new Lista(); }

public void insertaArista (int v1, int v2, int peso) { Celda item = new Celda (v2, peso);

this.adj[v1].insertar (item); }

(16)

TAD - Grafos Listas de adyacencia - usando punteros

Listas de adyacencia - usando punteros - implementacion

public boolean existeArista (int v1, int v2) { Celda item = new Celda (v2, 0);

return (this.adj[v1].busqueda(item) != null); }

public boolean listaAdjVacia (int v) { return this.adj[v].vacia (); }

public Arista primeroListaAdj (int v) { Celda item = (Celda) this.adj[v].primero ();

return item != null ? new Arista (v, item.vertice, item.peso) : null; }

public Arista proxAdj (int v) {

Celda item = (Celda) this.adj[v].proximo ();

return item != null ? new Arista (v, item.vertice, item.peso) : null; }

public Arista eliminaArista (int v1, int v2) throws Exception { Celda clave = new Celda (v2, 0);

Celda item = (Celda) this.adj[v1].eliminar (clave); return item != null ? new Arista (v1, v2, item.peso) : null; }

(17)

TAD - Grafos Listas de adyacencia - usando punteros

Listas de adyacencia - usando punteros - implementacion

public void imprimir () {

for (int i = 0; i < this.numVertices; i++) { System.out.println ("Vertice " + i + ":"); Celda item = (Celda) this.adj[i].primero (); while (item != null) {

System.out.println (" " + item.vertice + " (" +item.peso+ ")"); item = (Celda) this.adj[i].proximo ();

} } }

public int numVertices () { return this.numVertices; } public Grafo grafoTranspuesto () {

Grafo grafoT = new Grafo (this.numVertices); for (int v = 0; v < this.numVertices; v++)

if (!this.listaAdjVacia (v)) { Arista adj = this.primeroListaAdj (v); while (adj != null) {

grafoT.insertaArista (adj.v2 (), adj.v1 (), adj.peso ()); adj = this.proxAdj (v); } } return grafoT; } }

(18)

´

Arbol de recubrimiento m´ınimo

´

Arbol de recubrimiento m´ınimo

Aplicaciones

Proyecto de comunicaciones conectando n localidades.

Rutas ´

areas con menor costo que conecten todas las ciudades.

Se deben realizar n − 1 conexiones, conectando dos localidades en

cada una.

Dentro de todas las posibilidades de conexiones, se debe encontrar la

que utiliza la menor cantidad de cables o vuelos.

Modelamiento: Sea G = (V, A) un grafo conectado no direccionado,

donde V es el conjunto de ciudades, y A las posibles conexiones.

p(u, v) es el costo de conectar la ciudad u y v.

Soluci´

on: se debe encontrar un subconjunto de aristas T ⊆ A que

conecta todos los v´

ertices del grafo G y cuyo peso total

p(T ) =

P

(u,v)∈T

p(u, v) es minimizado.

(19)

´

Arbol de recubrimiento m´ınimo

´

Arbol de recubrimiento M´ınimo

Como G

0

= (V, T ) es ac´ıclico y conecta todos los v´

ertices, T forma

un ´

arbol generador o ´

arbol de recubrimiento de G, ya que T

genera o recubre el grafo G.

Este problema es conocido como ´

Arbol Generador M´ınimo (AGM) o

´

(20)

´

Arbol de recubrimiento m´ınimo

AGM - Algoritmos gen´

erico

void GenericoAGM 1 S = 0;

2 while (S no forme un ´arbol de recubrimiento m´ınimo) 3 (u, v) = selecciona (A) ;

4 if (arista (u, v) es segura para S ) then S = S + {(u, v)} 5 return S ;

(21)

´

Arbol de recubrimiento m´ınimo Algoritmo de Prim

(22)

´

Arbol de recubrimiento m´ınimo Algoritmo de Prim

AGM - Algoritmo de Prim

package Cap7;

public class FPHeapMinIndirecto { private double p[];

private int n, pos[], fp[];

public FPHeapMinIndirecto (double p[], int v[]) { this.p = p; this.fp = v; this.n = this.fp.length-1; this.pos = new int[this.n];

for (int u = 0; u < this.n; u++) this.pos[u] = u+1; }

public void rehacer (int izq, int der) { int j = izq * 2;

int x = this.fp[izq]; while (j <= der) {

if ((j < der) && (this.p[fp[j]] > this.p[fp[j + 1]])) j++; if (this.p[x] <= this.p[fp[j]]) break;

this.fp[izq] = this.fp[j]; this.pos[fp[j]] = izq; izq = j; j = izq * 2;

}

this.fp[izq] = x; this.pos[x] = izq; }

public void construir () { int esq = n / 2 + 1;

while (esq > 1) { esq--; this.rehacer (esq, this.n); } }

(23)

´

Arbol de recubrimiento m´ınimo Algoritmo de Prim

AGM - Algoritmo de Prim

public int retirarMin () throws Exception { int minimo;

if (this.n < 1) throw new Exception ("Error: heap vacio"); else {

minimo = this.fp[1]; this.fp[1] = this.fp[this.n]; this.pos[fp[this.n--]] = 1; this.rehacer (1, this.n); }

return minimo; }

public void diminuirClave (int i, double claveNueva) throws Exception { i = this.pos[i]; int x = fp[i];

if (claveNueva < 0)

throw new Exception ("Error: clave nueva con valor incorrecto"); this.p[x] = claveNueva;

while ((i > 1) && (this.p[x] <= this.p[fp[i / 2]])) { this.fp[i] = this.fp[i / 2]; this.pos[fp[i / 2]] = i; i /= 2; }

this.fp[i] = x; this.pos[x] = i; }

boolean vacio () { return this.n == 0; }

public void imprimir () { for (int i = 1; i <= this.n; i++)

System.out.print (this.p[fp[i]] + " "); System.out.println ();

} }

(24)

´

Arbol de recubrimiento m´ınimo Algoritmo de Prim

Algoritmo de Prim

La clase F P HeapM inIndirecto nos sirve para realizar un heap

indirecto.

El arreglo pos[v] mantiene la posici´

on del vertice dentro de un heap

f p, permitiendo que el v´

ertice v pueda ser accedido a un costo de

O(1).

Este acceso es necesario para disminuirClave.

(25)

´

Arbol de recubrimiento m´ınimo Algoritmo de Prim

Algoritmo de Prim

package Cap7;

import Cap7.listaadj.autoreferencia.Grafo; public class AlgPrim {

private int antecesor[]; private double p[]; private Grafo grafo;

public AlgPrim (Grafo grafo) { this.grafo = grafo; }

public void obtenerAGM (int raiz) throws Exception { int n = this.grafo.numVertices();

this.p = new double[n]; // int vs[] = new int[n+1]; //

boolean itemsHeap[] = new boolean[n]; this.antecesor = new int[n]; for (int u = 0; u < n; u ++) { this.antecesor[u] = -1; p[u] = Double.MAX_VALUE; // vs[u+1] = u; // itemsHeap[u] = true; }

(26)

´

Arbol de recubrimiento m´ınimo Algoritmo de Prim

Algoritmo de Prim

p[raiz] = 0;

FPHeapMinIndirecto heap = new FPHeapMinIndirecto (p, vs); heap.construir ();

while (!heap.vacio ()) {

int u = heap.retirarMin (); itemsHeap[u] = false; if (!this.grafo.listaAdjVacia (u)) {

Grafo.Arista adj = grafo.primeroListaAdj (u); while (adj != null) {

int v = adj.v2 ();

if (itemsHeap[v] && (adj.peso () < this.peso (v))) { antecesor[v] = u; heap.disminuirClave (v, adj.peso ()); }

adj = grafo.proxAdj (u); }

} } }

public int antecesor (int u) { return this.antecesor[u]; } public double peso (int u) { return this.p[u]; } public void imprimir () {

for (int u = 0; u < this.p.length; u++) if (this.antecesor[u] != -1)

System.out.println ("(" +antecesor[u]+ "," +u+ ") -- p:" + peso (u));

} }

(27)

´

Arbol de recubrimiento m´ınimo Algoritmo de Prim

Algoritmo de Prim - An´

alisis

El cuerpo interno del blucle es ejecutado |V | veces.

El m´

etodo rehacer tiene un costo de O(log |V |).

Entonces, el tiempo total para ejecutar la operaci´

on retirarM in es

O(|V | log |V |).

El while interno para recorrer la lista de adyacencia es: O(|A|).

La operaci´

on disminuirClave tiene un costo de O(log |V |).

Entonces, el tiempo total para ejecutar el algoritmo es:

O(|V | log |V | + |A| log |V |).

(28)

´

Arbol de recubrimiento m´ınimo Algoritmo de Kruskal

Algoritmo de Kruskal

Siempre adiciona a la soluci´

on, la arista de menor peso que conecta

dos vertices distintos.

Al inicio del algoritmo se ordenan las aristas por el peso.

(29)

´

Arbol de recubrimiento m´ınimo Algoritmo de Kruskal

Algoritmo de Kruskal

Sean C

1

y C

2

dos subarboles conectados por los v´

ertices (u, v).

Si (u, v) es una arista ligera que permite la conexi´

on entre dos ´

arboles,

entonces es una arista segura.

Mediante este procedimiento, se obtiene un ´

Arbol de Recubrimiento

M´ınimo, a˜

nadiendo en cada paso la arista de menor peso que conecta

dos ´

arboles y no forma un ciclo.

Inicia con |V | ´

arboles de un s´

olo vertice. En |V | pasos, une dos

´

(30)

´

Arbol de recubrimiento m´ınimo Algoritmo de Kruskal

Algoritmo de Kruskal

Sean C

1

y C

2

dos subarboles conectados por los v´

ertices (u, v).

Si (u, v) es una arista ligera que permite la conexi´

on entre dos ´

arboles,

entonces es una arista segura.

Mediante este procedimiento, se obtiene un ´

Arbol de Recubrimiento

M´ınimo, a˜

nadiendo en cada paso la arista de menor peso que conecta

dos ´

arboles y no forma un ciclo.

Inicia con |V | ´

arboles de un s´

olo vertice. En |V | pasos, une dos

´

arboles hasta que exista un s´

olo ´

arbol.

(31)

´

Arbol de recubrimiento m´ınimo Algoritmo de Kruskal

Algoritmo de Kruskal

void Kruskal (Grafo grafo)

ConjuntoDisjunto conj = new ConjuntoDisjunto ( ) ; 1. S = {};

2. for ( int v=0; v<grafo.numVertices() ; v++) conj.creaConjunto(v) ; 3. Ordena las aristas por Peso;

4. for (cada (u, v) de A tomadas en orden ascendente del peso) 5. if ( conj.encontraConjunto (u) != conj.encontraConjunto (v) ) 6. S = S + {(u, v)};

(32)

´

Arbol de recubrimiento m´ınimo Algoritmo de Kruskal

Algoritmo de Kruskal - Complejidad

Tambi´

en es de orden O(|A| log |V |)

Referencias

Documento similar

En el Cap´ıtulo 2 y en el 3 hay una componente que est´a acotada y que por tanto podemos considerar asint´oticamente (seg´un nos acercamos al tiempo de explosi´on)

Tras el estudio de la plataforma y entorno de desarrollo Android as´ı como el funciona- miento y estructura del est´ andar SCORM o las aplicaciones educativas para ejecutar

En este cap´ıtulo se ha desarrollado una propuesta de Broker para servicios de miner´ıa de datos en Cloud Computing llamada BrokerMD. BrokerMD es una propuesta formada por un

Estos algoritmos se encuentran programados en Matlab, por lo que el primer objetivo de este trabajo es migrar estos algoritmos a un lenguaje en el cual puedan

En este cap´ıtulo se ha reflejado todo el trabajo de investigaci´ on desem- pe˜ nado con el objetivo de desarrollar un sistema para el an´ alisis filogen´ etico completo con

ˆ La sinopsis presentada en el cap´ıtulo 3, es de gran utilidad, para el correcto enten- dimiento del trabajo, las definiciones y teoremas presentados all´ı, permiten entender

Aunque es posible transformar los grafos en árboles para después aplicar sobre estos últimos los algoritmos de búsqueda analizados, en ocasiones esta transformación no

Como se puede observar en los esquemas realizados sobre los datos obtenidos, la participante n´ umero uno antes de la aplicaci´ on del PIPNE seg´ un la investigadora, se comportaba