Semana 11
Exemplo:
Grafos podem ser:
Voltando ao exemplo:
De acordo com as definições anteriores, o grafo do exemplo exibido acima é direcionado, sem presos, cíclio e não simples.
O grafo do exemplo pode ser representado pelos conjuntos:
Grafos também podem ser representados através de matrizes de adjacência:
Um grafo pode ser implementado usando o código abaixo:
C/C++:
struct edge_s {
int v;
float weight;
};
struct graph_s {
edge_s edges[MAX_NVERTICES][MAX_DEGREE];
int degree[MAX_NVERTICES];
float weight[MAX_NVERTICES];
int nvertices;
int nedges;
};
Em uma busca em largura, controlamos os vértices descobertos e processados através de uma estrutura de fila.
Pseudocódigo:
BFS(G, s)
#initialize status, parent, and distance arrays;
#initialize queue Q;
Enqueue(Q, s)
status[s] = DISCOVERED;
while Q not empty:
u = Dequeue(Q);
#process vertex u;
status[u] = PROCESSED;
for each v adjacent to u:
if {u, v} is a valid edge:
#process edge {u, v};
if status[v] == UNDISCOVERED:
Enqueue(Q, v);
status[v] = DISCOVERED;
distance[v] = distance[u] + 1;
parent[v] = u;
Em uma busca em profundidade, controlamos os vértices descobertos e processados através de uma estrutura de pilha.
Pseudocódigo:
DFS(G, s)
#initialize status, parent and depth arrays;
#initialize stack S;
Push(S, s)
status[s] = DISCOVERED;
while S not empty:
u = Pop(S);
#process vertex u;
status[u] = PROCESSED;
for each v adjacent to u:
if {u, v} is a valid edge:
#process edge {u, v};
if status[v] == UNDISCOVERED:
Push(S, v);
status[v] = DISCOVERED;
depth[v] = depth[u] + 1;
parent[v] = u;
Exemplo (ordem - $5,4,2,3,1,0$):
A ordenação topológica pode ser implementada usando o seguinte pseudocódigo:
Pseudocódigo:
TopSort(G)
#initialize indegree, and sorted arrays;
#initialize queue Q;
for each v in G:
if indegree[v] == 0:
Enqueue(Q, v)
count = 0;
while Q not empty:
x = Dequeue(Q);
sorted[count] = x;
for each y adjacent to x:
indegree[y]--;
if indegree[y] == 0:
Enqueue(Q, y)
count++;
#assert count equals number of vertices;
Exemplo de árvore geradora mínima:
A árvore geradora mínima pode ser implementada usando o algoritmo de Prim, apresentado abaixo:
Pseudocódigo:
Prim(G, start)
#initialize intree, distance, and parent arrays;
v = start;
distance[start] = 0;
while intree[v] == FALSE:
intree[v] = TRUE;
for each w adjacent to v:
weight = weight of edge {v, w};
if (distance[w] > weight) and (intree[w] == FALSE):
parent[w] = v;
distance[w] = weight;
v = vertex not in tree, and min. distance
Observação:
Existem variações como, por exemplo, arvores geradoras máximas. Basta usar o algoritmo de Prim com pesos negativos.
O caminho mais curto pode ser implementada usando o algoritmo de Dijkstra, apresentado abaixo:
Pseudocódigo:
Dijkstra(G, start)
#initialize intree, distance, and parent arrays;
v = start;
distance[start] = 0;
while intree[v] == FALSE:
intree[v] = TRUE;
for each w adjacent to v:
weight = weight of edge {v, w};
if distance[w] > distance[v] + weight:
distance[w] = distance[v] + weight;
parent[w] = v;
v = vertex not in tree, and min. distance
O tamanho do caminho mais curto pode ser implementada usando o algoritmo de Floyd-Warshall, apresentado abaixo:
Pseudocódigo:
FloydWarshall(G)
for each vertex k in G:
for each vertex i in G:
for each vertex j in G:
if G[i,k] + G[k,j] < G[i,j]:
G[i,j] = G[i,k] + G[k,j];
O fluxo máximo entre dois vértices pode ser implementado usando o algoritmo de Ford-Fulkerson, apresentado abaixo:
Pseudocódigo:
FordFulkerson(G, source, sink)
max_flow = 0;
while BFS(G, source, sink, parent):
path_flow = MAX_VALUE;
for each v in the path from skin to source:
u = parent[v]
path_flow = min(path_flow, G[u,v]);
for each v in the path from sink to source:
u = parent[v];
G[u,v] -= path_flow;
G[v,u] += path_flow;
max_flow += path_flow;
Leitura:
Exercícios:
Semana 11